Shell中带引号的"$@"展开为多参数的原因及解决方法
关于sh -c中$@参数传递的问题解答
先看两个实际执行的命令差异:
- 执行这个命令会正常输出
foo bar:
sh -c 'printf "$@"' -- "foo bar"
- 但把参数拆成
foo和bar分开传时,只会输出foo:
sh -c 'printf "$@"' -- foo bar
(sh和bash的表现一致)
问题1:为什么会出现这种情况?
你对引号的理解有个小偏差:引号是shell用来标记「单个参数边界」的工具,不是字符串本身的一部分。而$@的作用是把脚本的所有位置参数(也就是--后面的内容)逐个原样传递给后续命令。
具体来说:
- 当你传
-- "foo bar"时,sh -c的脚本拿到的位置参数只有1个:$1="foo bar",printf "$@"就等价于printf "foo bar",自然输出完整内容。 - 当你传
-- foo bar时,脚本拿到的位置参数是2个:$1="foo"、$2="bar",printf "$@"就等价于printf foo bar。而printf的规则是第一个参数作为格式字符串,后面的参数是填充格式的变量。这里foo里没有占位符(比如%s),所以printf只会输出格式字符串本身,第二个参数bar直接被忽略,最终只输出foo。
问题2:如何修改命令实现期望行为?
要让多个参数合并成单个字符串传给printf,有两种简单方法:
- 用
$*代替$@:$*会把所有位置参数用系统默认的分隔符(默认是空格)拼接成一个字符串。命令如下:
sh -c 'printf "$*"' -- foo bar
- 显式指定printf的格式字符串,再用
$*传递合并后的内容(更严谨,避免格式字符串意外):
sh -c 'printf "%s" "$*"' -- foo bar
内容的提问来源于stack exchange,提问作者ChrisB
相关产品推荐
相关产品推荐

