17

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?

4 Answers4

22

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.

Ijaz Ahmad
  • 7,202
  • 2
    A stricter, standard conformant way would be sh -c 'echo $1' echo foo – jlliagre Jan 05 '16 at 16:33
  • 1
    sh -c 'echo "$@"' fnord a b c d ... – zwol Jan 05 '16 at 17:43
  • 3
    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' – X3MBoy Jan 05 '16 at 18:01
  • 1
    @ X3MBoy The questioner already know that /bin/sh -c 'echo foo' works fine. He wanted to echo something outside that , which i explained – Ijaz Ahmad Jan 05 '16 at 18:18
  • @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 ? – Subhajit Apr 17 '20 at 10:51
19
$ 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
Marco
  • 33,548
8

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".

chaos
  • 48,171
3

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.