/bin/bash -c与直接执行命令有何不同?

18

我很好奇为什么这个命令:

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

输出 /mydir 目录中最后的十个文件,但是

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

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

2个回答

15

你正在使用双引号,因此父shell会在将参数传递给/bin/bash之前插入反引号和变量。

因此,你的/bin/bash将接收以下参数:

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

这是一种语法错误。

为了避免这种情况,使用单引号来传递参数:

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

1
偶然间,这个答案帮助我解决了一个奇怪的错误(部署/Capistrano/Composer等),根本原因:在某个地方自动拼接的命令中使用了双引号和单引号。谢谢。 - Wolfram

2
您的子命令输出中有不同的换行符处理方式。例如,在我的机器上,使用/bin,您的第一个示例输出如下:
rmdir
sh
sleep
stty
sync
tcsh
test
unlink
wait4path
zsh

但是对于后者:
/bin/bash: -c: line 1: syntax error near unexpected token `sh'
/bin/bash: -c: line 1: `sh'

因为命令替换在两种情况下都发生在你的第一个 shell 中,所以你的第一个例子起作用了(当创建命令行时,换行符被删除了),但是在第二个例子中却不起作用—— 它们保留在字符串中,这要归功于你的""。使用 echo 而不是 bash -c 可以展示这一点:

$ 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

从中可以看出你的 bash -c 会看到什么,以及为什么它不起作用——shdo 之前!

你可以使用单引号代替,但这将导致子命令在新的子 shell 中运行:

$ /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

如果这不行的话,你需要消除这些换行符:

$ /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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接