1

Was trying to create a text block that I could just copy and paste into a Terminal window to accomplish something (in this case, create a systemd script to do maintenance on MySQL dbs, create a timer file to run that script weekly, and to enable the script).

When pasting it into a Terminal, it displays half of the script (up until the first EOF), prompts for the password through read, and then copies the typed password, along with the rest of the script in the spot where $pass is in the first file.

This is the block:

sudo sync && echo 'Enter MySQL password for Maintenance user' && read pass && sudo bash -s -c 'cat > "/usr/lib/systemd/system/db-m.service"' << "EOF"
[Service]
Type=oneshot
ExecStart='/usr/bin/mysqlcheck' --auto-repair --optimize --all-databases --force -u'maintenance' -p'$pass'
ExecStart='/usr/bin/sync'
EOF
sudo bash -c 'cat > "/usr/lib/systemd/system/db-m.timer"' << EOF
[Unit]
Description=Weekly database repair and maintenance

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable 'db-m.timer' && sudo systemctl start 'db-m' 'db-m.timer' && sudo systemctl status 'db-m' -l

I recently added the "read pass" and the '$pass' variable that occurs 3 lines later; without those two things, I can copy and paste the entire block no problem.

  • Copy & Paste is very desktop environment dependent action. Are you trying to do this copy and paste on a Linux desktop environment ? Likes of Gnome/KDE/Xfce etc. ? Or you are on a windows or Mac computer, using a terminal emulator ? In either case, details about your environment, such as name and version numbers, would be helpful. – MelBurslan Apr 28 '16 at 15:45
  • I'd use expect, as that can enter one command, check and wait for the result, do the next command, etc. Pasting will run into all sorts of fun interactions depending on what reads or writes what when, buffer sizes, etc. – thrig Apr 28 '16 at 15:47
  • just to emphasise what @Gilles said: what you should do is put this in a script, with '#!/bin/sh' at the top. don't paste it directly into your shell. – cas Apr 29 '16 at 0:24
0

The shell running in the terminal receives the script that you're pasting on its standard input, and your script itself reads from standard input. There's a conflict here: your script will end up reading a bit of itself.

If you don't get a sudo prompt, then what happens is:

  1. The shell reads whole lines until it has a complete command. The first line starts a here document, so the shell keeps reading the here document.
  2. When the shell reaches the first EOF line, it has a complete command, so it executes it.
  3. sudo sync and `echo '…' run.
  4. read pass runs. It reads a line of input, which is sudo bash -c …. This sets pass.
  5. sudo sync runs.
  6. The shell has finished executing the commands it had read, so it reads the next line of input which is [Unit].

Don't paste complex multiline shell code into a terminal. Instead, if you really need to run the clipboard content as a shell script, use xsel or xclip under X11, or pbpaste on OSX.

eval "`xsel`"
eval "`xclip`"
eval "`pbpaste`"

But really, what you should do is put this in a script, with #!/bin/sh at the top.

  • Wouldnt fc, or \C-x\C-e be an easy solution to this problem? – Steven Penny May 7 '16 at 1:16
  • @StevenPenny Feels more cumbersome to me than eval "`xsel`", but it does have the benefit (dubious, for a multiline command in bash) of entering the command in the history. – Gilles May 7 '16 at 10:48
  • shopt cmdhist/lithist? – Steven Penny May 7 '16 at 15:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.