Why doesn't echo called as /bin/sh -c echo foo output anything?

9,613

Solution 1

From man sh

-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

It means your command should be like this:

 $ sh -c 'echo "$0"' foo 
 foo

Similarly:

$ sh -c 'echo "$0 $1"' foo bar
foo bar

That was the first part to understand; the second case is simple and doesn't need explanation, I guess.

Solution 2

$ echo foo
foo

This calls echo with the argument foo and foo is printed.

$ /bin/sh -c echo foo

This invokes the shell with the argument echo and provides foo as argument $0. The echo outputs a new line and you discard the foo. If you want to output foo, quote the argument:

sh -c 'echo foo'

or use the provided argument:

sh -c 'echo $0' foo

In this example

$ /bin/sh -c 'echo foo; echo bar'
foo
bar

The shell is invoked with the argument echo foo; echo bar which outputs

foo
bar

Solution 3

In this command:

echo foo

echo is the binary (or built-in command) and foo is the first argument.

Here:

/bin/sh -c echo foo

/bin/sh is the binary, whose first argument is -c, which itself accepts a "command string" as parameter. This is echo in the above example. Then there is a third argument: foo which is an argument for /bin/sh, not for echo. That's why in your third example:

/bin/sh -c 'echo foo; echo bar'

... both are printed. You quoted the argument. Thus: the first argument is -c, and the parameter to this argument is 'echo foo; echo bar' which is interpreted whole as one argument; as the "command string".

Solution 4

The structure sh -c word executes only word (in a shell).
Added words mean other things, like argument zero, one, two, etc.:

sh -c word zero one two three

to keep a command which has spaces as one word, quoting is needed:

sh -c 'echo fnord' zero one two three

so, this prints all the arguments:

$ sh -c 'echo "args=" "$0" "$@"' zero one two three
args = zero one two three

Examples

In the example you present: /bin/sh -c echo foo The first word (after options) is echo, that is what is executed. And an echo with no text will print a new-line only, nothing else:

$ /bin/sh -c echo foo

That is why you do get an empty line.

If you quote the space, you will be executing "one word" (no spaces), as this:

$ /bin/sh -c echo\ foo
foo
$ /bin/sh -c "echo foo"
foo
$ /bin/sh -c 'echo foo'
foo

Conclusion

Keep the executed command as one "word" using quoting.

Share:
9,613

Related videos on Youtube

SilverlightFox
Author by

SilverlightFox

Updated on September 18, 2022

Comments

  • SilverlightFox
    SilverlightFox almost 2 years

    For example, while this works:

    $ echo foo
    foo
    

    This doesn't:

    $ /bin/sh -c echo foo
    
    

    Whereas this does:

    $ /bin/sh -c 'echo foo; echo bar'
    foo
    bar
    

    Is there an explanation?

  • jlliagre
    jlliagre over 8 years
    A stricter, standard conformant way would be sh -c 'echo $1' echo foo
  • zwol
    zwol over 8 years
    sh -c 'echo "$@"' fnord a b c d ...
  • X3MBoy
    X3MBoy over 8 years
    IMHO the explanation was quite good, but sh -c 'echo $0' foo is not the best option, taking into account that the questioner already know that /bin/sh -c 'echo foo; echo bar' works, you can simply answer quoting the command /bin/sh -c 'echo foo'
  • Ijaz Ahmad
    Ijaz Ahmad over 8 years
    @ X3MBoy The questioner already know that /bin/sh -c 'echo foo' works fine. He wanted to echo something outside that , which i explained
  • Subhajit
    Subhajit about 4 years
    @IjazAhmadKhan what if I want to execute some command with sh -c something like this: sh -c test.py arg1 arg2 arg3 ? Secondly what if arg3 contains something like "/export/local/repo/ab#cd.rpm" , how can I handle # in this case ?