JivanAmara
JivanAmara

Reputation: 1115

How does /bin/bash -c differ from executing a command directly?

I'm curious why the commmand:

for f in `/bin/ls /mydir | sort | tail -n 10`; do echo $f; done;

Outputs the last ten files in /mydir, but

/bin/bash -c "for f in `/bin/ls /mydir | sort | tail -n 10`; do echo $f; done;"

Outputs "syntax error near unexpected token '[file in /mydir]'"

Upvotes: 20

Views: 27988

Answers (2)

nneonneo
nneonneo

Reputation: 179402

You are using double-quotes, so the parent shell is interpolating backticks and variables before passing the argument to /bin/bash.

Thus, your /bin/bash is receiving the following arguments:

-c "for f in x
y
z
...
; do echo ; done;"

which is a syntax error.

To avoid this, use single quotes to pass your argument:

/bin/bash -c 'for f in `/bin/ls /mydir | sort | tail -n 10`; do echo $f; done;'

Upvotes: 15

Carl Norum
Carl Norum

Reputation: 224854

Different newline handling in your subcommand output. For example on my machine, using /bin I get this output for your first example:

rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh

But with the latter:

/bin/bash: -c: line 1: syntax error near unexpected token `sh'
/bin/bash: -c: line 1: `sh'

Because the command substitution takes place in your first shell in both cases, your first one works (the newlines are stripped out when making the command line), but in the second case it doesn't - they remain in the string thanks to your "". Using echo rather than bash -c can showcase this:

$ echo "for f in `/bin/ls /bin | sort | tail -n 10`; do echo \$f; done"
for f in rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh; do echo $f; done

You can see from that what your bash -c is seeing and why it doesn't work - the sh comes before the do!

You can use single quotes instead, but that will cause the subcommand to run in your new subshell:

$ /bin/bash -c 'for f in `/bin/ls /bin | sort | tail -n 10`; do echo $f; done'
rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh

If that's not ok, you need to get rid of those newlines:

$ /bin/bash -c "for f in `/bin/ls /bin | sort | tail -n 10 | tr '\n' ' '`; do echo \$f; done"
rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh

Upvotes: 2

Related Questions