37

I'm trying to remote login to a shell and execute a bunch of commands on the shell. But to make it more readable, I'd like to place my code over multiple lines. How should I be doing this?

ssh -o <Option> -x -l <user> <host> " $long_command1; $long_command2; ....  "

Thanks!

5 Answers 5

57

ssh is in fact just passing a string to the remote host. There this string is given to a shell which is supposed to interpret it (the user's login shell, which is typically something like bash). So whatever you want to execute needs to be interpretable by that remote login shell, that's the whole rule you have to stick to.

You can indeed just use newlines within the command string:

ssh alfe@sweethome "
  ls /home/alfe/whatever
  ping foreignhost
  date
  rm foobar
"
Sign up to request clarification or add additional context in comments.

1 Comment

You might want to have a look at this answer I gave just recently about passing variables into ssh-remote shells.
34

You can use the Here Documents feature of bash. It is like:

ssh <remote-host> bash <<EOF
echo first command
echo second command
EOF

EOF marks the end of the input.

For further info: use man bash and search for Here Documents.

Edit: The only caveat is that using variables can be tricky, you have to escape the $ to protect them to be evaluated on the remote host rather then the local shell. Like \$HOSTNAME. Otherwise works with everything that is run from bash and uses stdin.

5 Comments

is it the remote user's bash shell that'll be used here?
@ShyamaDorbala: Yes, remote bash. It actually works without bash as well, just "HereDocing" to ssh.
interesting. I'll check it out. Thanks!
I like this solution, but I wish it worked better with indentation. Leading spaces will break it (but leading tabs is OK).
As an alternative to the escaping, you can use <<"EOF" to stop the evaluation in the local shell.
3

You can do like in the following example.

ssh -o <Option> -x -l <user> <host> '
pwd
whoami
ls
echo "$PATH"
'

Comments

1

This works on Ubuntu 24.04 host and server

  • runs under sudo
  • runs multiple lines
  • does not require me to enter my sudo password

Some of these came from other answers on SO and on other sites. I did have to play with those answers until it worked as I needed it:

  • your_userid: the non-sudo userid on the remote host
  • your_remote_host: the ip address or domain of the remote host
  • the file secure_sudo_pwd has the sudo password in it
my_cool_sudo_password    <= ensure there is newline after this
  • the first cat puts the sudo password into the stdin stream and the rest of your commands until "EOF"
  • invokes SSH and then cats the stdin into the next command, which is sudo su -
  • the backslash in \| in required (it's not a markdown for SO posts).
  • the prompt prevents most of the initial dump that bash does from showing up
  • note that comments in the stdin work correctly, the lines are ignored
cat secure_sudo_pwd - << EOF | ssh $your_userid@$your_remote_host cat \| sudo --prompt="" -S -- su -
hostname
whoami
# echo commented out line
hostname
whoami
echo line1
EOF

typical output:

vm-1     <= my host is a virtualbox running Ubuntu 24.04
root     <= confirms that it is running as root
root
vm-1
line1

I have checked as best as I can whether the password or content of this SSH session shows up on the host:

# on the host
cd /var/log
tail -f dmesg syslog auth.log kern.log

There were no indications of "sudo", "hostname", "whoami", or password, etc.

There was an entry in authlog showing an ssh login

Jan  7 12:55:06 vm-1 sshd[12122]: Accepted publickey for xx from 10.0.0.xx port 33698 ssh2: ED25519 SHA256:U24xx

Comments

1

The answers thus far have all left out a technique that's much less issue-prone than writing code in a heredoc, quoted string, or other environment that has subtly different behavior than the outer shell: Define a function, and let bash itself serialize that function into the arguments passed to ssh.

remoteFunc() {
  long_command 1 && ...
  long_command 2...
  if blah; then long_command 3; else meh; fi

  # In a single-quoted string, these quotes would disappear!
  echo "string with 'single quotes'"

  # in a heredoc or double quotes, remoteCommand would be run locally!
  remoteCmdOutput=$(remoteCommand)
}

ssh remoteHost "$(declare -f remoteFunc); remoteFunc"

If your function requires variables to also be passed over ssh, you can use declare -p to serialize them the same way:

myVar='...whatever...'
myOtherVar='...same...'
myFunc() {
  echo "Do something with $myVar and $myOtherVar here..."
}

ssh remoteHost "$(declare -f myFunc); $(declare -p myVar myOtherVar); myFunc"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.