目标和背景
我正在编写一个 bash 脚本(称为 foreach_repo
),它应该将传递的参数作为 shell 命令执行,例如:
foreach_repo hg status
应该执行命令hg status
。 (它在复杂的存储库嵌套结构上执行此操作,我无法控制这种结构,但我需要经常对它们进行批处理)。
这种类型的命令类似于 sudo
和其他“高阶”命令;例如 sudo hg status
将依次以 super 用户权限执行 hg status
。
在内部,我的脚本执行以下操作(给定标准输入上的存储库路径提要,由脚本的另一部分创建 - 与此问题无关):
while read repo do
container="$repo/.."
cd $base/$container
$@
done
其中 $@
用于将传递的参数解释为要在存储库中执行的命令。
执行
这种方法适用于简单的命令,例如
foreach_repo hg status
将正确返回结构中每个存储库的状态列表。然而,更复杂的命令(带有转义符、引号……)被搞砸了。例如,当我尝试
foreach_repo hg commit -m "some message"
我明白了
abort: message: no such file or directory
因为引号被去掉了,实际执行的命令是:
hg commit -m some message
尝试过的解决方案
手动转义引号或要传递的整个命令没有效果,使用 $*
代替 $@
也没有效果。但是,sudo
这样的命令可以处理这种情况,即:
sudo hg commit -m "some message"
会实际(并且正确地)执行
hg commit -m "some message"
我怎样才能完成相同的行为?
最佳答案
您走在正确的轨道上,并且几乎做到了。您只需要使用 "$@"
而不是 $@
。
下面是 $*
和 $@
的作用总结,带引号和不带引号:
$*
和$@
粘贴位置参数,然后将它们标记化(使用$IFS
)为单独的字符串。"$*"
将位置参数作为一个字符串粘贴,并在每个字符串之间插入$IFS
的第一个字符(通常是空格)。"$@"
粘贴位置参数,作为每个参数的字符串。
例子:
$ set foo bar "foo bar:baz"
$ printf "%s\n" $*
foo
bar
foo
bar:baz
$ printf "%s\n" $@
foo
bar
foo
bar:baz
$ printf "%s\n" "$*"
foo bar foo bar:baz
$ printf "%s\n" "$@"
foo
bar
foo bar:baz
以下是设置 $IFS
时发生的变化:
$ IFS=:
$ printf "%s\n" $*
foo
bar
foo bar
baz
$ printf "%s\n" $@
foo
bar
foo bar
baz
$ printf "%s\n" "$*"
foo:bar:foo bar:baz
$ printf "%s\n" "$@"
foo
bar
foo bar:baz
关于bash:获取传递给脚本的文字参数并将它们作为命令执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18333070/