Skip to content

SSH

Command quoting

Tip

Read POSIX (unix) basics first.

SSH only takes a simple string as command to send to the remote end 1.

A normal SSH-client joins the passed command and all arguments with a space as separator, and sends this as command string (with an "exec" request; if no command is passed it uses "shell" instead).

This means the following commands are equivalent:

ssh server printf '~%s~' value1 value2
ssh server printf '~%s~' "value1 value2"
ssh server 'printf ~%s~ value1 value2'
ssh server printf ~%s~ value1 value2

The ssh-server retrieves the shell for the user, e.g. /bin/bash, and runs execv("/bin/bash", ["-bash", "-c", command]), i.e. forwards the command string as one argument to $SHELL -c. (The leading - in argv[0] marks a login shell.)

This means the shell on the server splits arguments again and repeats expansions.

To actually get quoting through you need to use nested quotes:

ssh server 'printf "~%s~" "value1 value2"'

The %q format from printf can be used to automate quoting; a command can be fully quoted by wrapping it in "$(printf ' %q' ...)", like:

ssh server "$(printf ' %q' printf "~%q~" "value1 value2")"

As an alternative you can use the @Q parameter transformation; store the command to run in an array and then expand with quoted transformation:

cmd=(printf "~%q~" "value1 value2")
ssh server "${cmd[@]@Q}"

See sshsystem for a wrapper script that handles normal options to ssh and escapes the command.

If you actually need expansions on the server (like ~serveruser) you’re on your own…