0

Currently getting

touch: missing file operand
Try `touch --help' for more information.

I followed execute su -c over ssh to find out that I need to do ssh remote-server 'sh -c "touch 1 && echo 2"'. But I'd like to know why ... since all other commands work just fine like ssh remote-server echo -e 123

grosser
  • 267
  • 4
  • 8

1 Answers1

2

The command as you printed it in the question with the two layers of quoting should work as expected. To debug these kinds of problems, use 'ssh -v' and look for the "Sending command:" line to see what actually gets sent to the remote host. For example:

$ ssh -v remotehost 2>&1 'sh -c "touch 1 && echo 2"' | grep command
debug1: Sending command: sh -c "touch 1 && echo 2"

I am able to reproduce your error when I take out the single quotes:

$ ssh  remotehost 2>&1 sh -c "touch 1 && echo 2" 
touch: missing file operand

Debugging that with "ssh -v" as shown above, we get:

ssh -v remotehost 2>&1 sh -c "touch 1 && echo 2" | grep command
debug1: Sending command: sh -c touch 1 && echo 2

So what happened here is a standard shell quoting problem. With two layers of quotes (my working example above), here's how it gets parsed:

You type the command:

 ssh remotehost  'sh -c "touch 1 && echo 2"'

The shell parses it as three strings:

string1:  ssh
string2:  remotehost
string3:  sh -c "touch 1 && echo 2"

The shell then finds the "ssh" command, and runs it with two arguments (string2 and string3 above).

The "ssh" command then executes the contents of string3 on the remote host, which is broken down into 3 strings:

string1: sh
string2: -c
string3: touch 1 && echo 2

Now THAT shell now has to parse string3. It breaks it up into five tokens and interprets as you'd expect.

Without the double layer of quotes, the remote shell tries to execute:

sh -c touch 1 && echo 2

And if you paste that into your local command line, you'll see you get the same result. The "1" is being dropped due to some ambiguity with the shell's command-line parsing, even though the rest of the command gets parsed as expected. If you change the "&&" to "||" (so that both sides of the expression get executed) you'll see the "2" still gets printed out.

The moral of this story is that you never want to have one shell call another, because you will dive down the endless maze of problems with quoting, backslash escapes, variable expansion and parsing ambiguities just like you found out above. Alas, there's often no way to avoid these constructions, so if you have to do this, always quote carefully, and keep the expressions as simple as possible.

  • Thx! Did not expect to find a bug in "ssh" ... I was thinking I must be understanding something wrong :( – grosser Feb 12 '19 at 02:19