Below are three methods to send multiple line commands over SSH. The first method is a quick overview of running remote commands over SSH, the second method uses the
Contents
Running Remote Commands Over SSH
To run one command on a remote server over SSH:ssh $HOST lsTo run two commands on a remote server over SSH:
ssh $HOST 'ls; pwd'To run the third, fourth, fifth, etc. commands on a remote server over SSH keep appending commands with a semicolon inside the single quotes.
But, what if you want to remotely run many more commands, or if statements, or while loops, etc., and make it all readable?
#!/bin/bash ssh $HOST ' ls pwd if true; then echo "This is true" else echo "This is false" fi echo "Hello world" 'The above shell script works but begins to break if local variables are added.
For example, the following shell script will run, but the local variable
#!/bin/bash HELLO="world" ssh $HOST ' ls pwd if true; then echo $HELLO else echo "This is false" fi echo "Hello world" 'In order to parse the local variable
Using SSH with the BASH Command
As mentioned above, in order to parse the local variable#!/bin/bash HELLO="world" ssh $HOST bash -c "' ls pwd if true; then echo $HELLO else echo "This is false" fi echo "Hello world" '"Perhaps you want to use a remote sudo command within the shell script:
#!/bin/bash HELLO="world" ssh $HOST bash -c "' ls pwd if true; then echo $HELLO else echo "This is false" fi echo "Hello world" sudo ls /root '"When the above shell script is run, everything will work as intended until the remote sudo command which will throw the following error:
sudo: sorry, you must have a tty to run sudoThis error is thrown because the remote sudo command is prompting for a password which needs an interactive tty/shell. To force a pseudo interactive tty/shell, add the
#!/bin/bash HELLO="world" ssh -t $HOST bash -c "' ls pwd if true; then echo $HELLO else echo "This is false" fi echo "Hello world" sudo ls /root '"With a pseudo interactive tty/shell available, the remote sudo command’s password prompt will be displayed, the remote sudo password can then be entered, and the contents of the remote root’s home directory will be displayed.
However, recently I needed to run a specific remote sed command over SSH to find and delete one line and the subsequent three lines and another specific remote sed command over SSH to find a line and insert another line with some text above it, so I naturally tried using the
#!/bin/bash ssh $HOST bash -c "' cat << EOFTEST1 > /tmp/test1 line one line two line three line four EOFTEST1 cat << EOFTEST2 > /tmp/test2 line two EOFTEST2 sed -i -e '/line one/,+3 d' /tmp/test1 sed -i -e '/^line two$/i line one' /tmp/test2 '"Everytime I would run the above shell script, I would get the following error:
sed: -e expression #1, char 5: unterminated address regexHowever, the same commands work when run by themselves:
ssh $HOST "sed -i -e '/line one/,+3 d' /tmp/test1" ssh $HOST "sed -i -e '/^line two$/i line one' /tmp/test2"I thought the problem may be because of single quotes within single quotes. The
However, I debunked this single quote theory being my problem because running a simple remote sed search and replace command inside of the bash command worked just fine:
#!/bin/bash ssh $HOST bash -c "' echo "Hello" >> /tmp/test3 sed -i -e 's/Hello/World/g' /tmp/test3 '"I can only assume the problem with the specific remote sed commands is something with the syntax that I have not yet figured out.
Despite all this, I eventually figured out that the specific remote sed commands I wanted to run would work when using SSH with
Using SSH with HERE Documents
As mentioned above, the specific remote sed commands I wanted to run did work when using SSH with HERE documents:ssh $HOST << EOF cat << EOFTEST1 > /tmp/test1 line one line two line three line four EOFTEST1 cat << EOFTEST2 > /tmp/test2 line two EOFTEST2 sed -i -e '/line one/,+3 d' /tmp/test1 sed -i -e '/^line two$/i line one' /tmp/test2 EOFDespite the remote sed commands working, the following warning message was thrown:
Pseudo-terminal will not be allocated because stdin is not a terminal.To stop this warning message from appearing, add the
ssh -T $HOST << EOF cat << EOFTEST1 > /tmp/test1 line one line two line three line four EOFTEST1 cat << EOFTEST2 > /tmp/test2 line two EOFTEST2 sed -i -e '/line one/,+3 d' /tmp/test1 sed -i -e '/^line two$/i line one' /tmp/test2 EOFWith this working, I later discovered remote sudo commands that require a password prompt will not work with HERE documents over SSH.
ssh $HOST << EOF sudo ls /root EOFThe above ssh command will throw the following error if the SSH user you are logging into requires a password when using the remote sudo command:
Pseudo-terminal will not be allocated because stdin is not a terminal. user@host's password: sudo: no tty present and no askpass program specifiedHowever, the remote sudo command will work if the SSH user’s sudo settings allow that user to use sudo without a password by setting
References
What’s the Cleanest Way to SSH and Run Multiple Commands in Bash?Chapter 19. Here Documents
You sir... Are brilliant. You've covered everything I stuggled with. Thank you very much. Really readable article.
ReplyDelete