The first command of a nested compound command receives no arguments

Timo Kilpilehto timperoinen at gmail.com
Sun Oct 21 22:10:52 AEDT 2018


On Sun, Oct 21, 2018 at 9:06 AM Parke <parke.nexus at gmail.com> wrote:
> The following three commands produce unexpected behavior.
> Specifically, it appears that the first nested command is called with
> no arguments.
>
> root at cosmic:~# ssh localhost dash -c 'echo 1 && echo 2 && echo 3'
>
> 2
> 3
> root at cosmic:~# ssh localhost dash -c 'touch a && touch b && touch c'
> touch: missing file operand
> Try 'touch --help' for more information.
> root at cosmic:~# ssh localhost dash -c 'touch a b && touch c d && touch e f'
> touch: missing file operand
> Try 'touch --help' for more information.
>
> Is the above the intended behavior?  If so, why?  What is going on?
> It seems strange to me.

Hi Parke,

This has nothing to do with openssh or even chaining commands really.
We can see similar behaviour when just using eval and sh -c:

eval sh -c "echo none of this makes any difference"
-> prints empty line

So, what happens here?

       The eval utility shall construct a command by concatenating arguments
       together, separating each with a <space> character.  The constructed
       command shall be read and executed by the shell.

So, in short what happens is this gets executed:
sh -c echo none of this makes any difference
-> again, prints empty line

And why does this happen you might ask?

Here's what man page says about option -c:

       -c string If  the  -c  option  is  present, then commands are read from
                 string.  If there are arguments after the  string,  they  are
                 assigned to the positional parameters, starting with $0.

In other words what this means is that shell will execute the sole
command (echo) we provided in a sub shell as an inline script. Then
the rest of the arguments if any will be provided to the script as
positional arguments starting with $0. However, our script does not
use them for anything, it just runs echo with no arguments. In order
to make it output these arguments we'd have to do something like this:

sh -c 'echo $@' _ now something gets printed
-> outputs "now something gets printed"

In the end it may just be easier to use quoting on the entire inline
script to execute:
sh -c "echo now something gets printed"

And in order to persist the quoting through evaluating using eval or
ssh or anything of the sort we need extra layer of quoting:
eval 'sh -c "echo now something gets printed"'

So to summarise ssh handles in this case the command precisely as if
using eval which I would say is quite likely the intended behaviour
and exactly what one would expect.

I hope this clarifies things for you.

Best regards,
Timo


More information about the openssh-unix-dev mailing list