我正在编写一个bash脚本(称为foreach_repo
),该脚本应该作为shell命令执行传递的参数,例如:
foreach_repo hg status
应该执行命令hg status
。 (它在一个复杂的存储库嵌套结构上执行此操作,我无法控制此结构,但我需要经常批处理它们。)
这种类型的命令类似于sudo
和其他“高阶”命令;例如,sudo hg status
将依次以超级用户权限执行hg status
。
在内部,我的脚本执行以下操作(给定stdin上的存储库路径的提要,由脚本的另一部分创建 - 与此问题无关):
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"
如何实现相同的行为?
答案 0 :(得分:7)
你走在正确的轨道上,几乎得到了它。您只需使用"$@"
代替$@
。
以下是$*
和$@
执行操作的摘要,包括和不包含引号:
$*
和$@
粘贴位置参数,然后将它们(使用$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