![eval " command \"$@\" "、"$@" 和 "$(echo $@)" 用作参数时的不同行为](https://linux22.com/image/71857/eval%20%22%20command%20%5C%22%24%40%5C%22%20%22%E3%80%81%22%24%40%22%20%E5%92%8C%20%22%24(echo%20%24%40)%22%20%E7%94%A8%E4%BD%9C%E5%8F%82%E6%95%B0%E6%97%B6%E7%9A%84%E4%B8%8D%E5%90%8C%E8%A1%8C%E4%B8%BA.png)
我想通过任意名为 的函数以编程方式将多个参数传递给 Tor 浏览器 (firefox) tor
。这是为了命令tor search terms
,瞧!我的搜索词。当使用这三种变体时,
~/tor-browser_en-US/Browser/start-tor-browser -search "$(echo $@)"
eval "~/tor-browser_en-US/Browser/start-tor-browser -search \"$@\""
~/tor-browser_en-US/Browser/start-tor-browser -search "$@"
只有前两行似乎执行相同。它们会导致单个 Tor 浏览器窗口搜索任意文本内容的术语。然而,第三行会为每个单词产生一个新的搜索窗口。
为什么会有这种行为?
让我澄清一下。我不是在寻找解决方法,而是只是对有关 bahviour 的解释感兴趣。
答案1
对于第三个版本,您"$*"
不需要"$@"
.
解释
为了说明这一点,让我们设置一些位置参数:
$ set -- arg1 arg2 arg3
现在,让我们用你的echo
公式读出它们:
$ printf "%s\n" "$(echo $@)"
arg1 arg2 arg3
让我们看看"$@"
它们做了什么:
$ printf "%s\n" "$@"
arg1
arg2
arg3
区别在于"$@"
扩展为三个单词而"$(echo $@)"
扩展为单个字符串。如果您想要类似的行为"$(echo $@)"
,您应该使用"$*"
:
$ printf "%s\n" "$*"
arg1 arg2 arg3
文档
从man bash
:
"$*"
产生单个字符串:
“$*”相当于“$1c$2c...”,其中 c 是 IFS 变量值的第一个字符。
相比之下,产生一系列单词的man bash
状态:$@
“$@”相当于“$1”“$2”...
使用评估
观察:
$ echo 'printf "%s\n" "' "$@" '"'
printf "%s\n" " arg1 arg2 arg3 "
当 bash 计算echo
命令时,"$@"
扩展为单独的单词。 echo
然而,将这些单独的单词组合成一个字符串。我们之前在查看时看到了这一点"$(echo $@)"
。 eval
工作原理类似。它从所有参数中生成一个字符串,并计算该字符串。因此:
$ eval 'printf "%s\n" "' "$@" '"'
arg1 arg2 arg3
这也被记录在案man bash
:
评估[参数...]
args 被读取并连接在一起形成一个命令。然后该命令由 shell 读取并执行,...
答案2
考虑一个更简单的例子来看看区别:
$ set "a b" c "d e"
$ printf "%s\n" "$@"
a b
c
d e
前面的就是你说的应该使用;它简单、容易理解并且正确。
$ printf "%s\n" "$(echo $@)"
a b c d e
这里$@
首先展开未引用的(命令替换周围的引号是单独的,尚未应用),因此它相当于$(echo a b c d e)
, 并且printf
在格式字符串后面有一个附加参数。
$ eval "printf \"%s\n\" \"$@\""
a b c d e
此处,$@
被引用,但扩展会为eval
( printf "%s\n" a b
、c
和d e
) 生成 3 个单独的参数,这些参数连接成一个命令,生成与上面的数字 2 相同的输出。