3

目标和背景

我正在编写一个 bash 脚本(称为foreach_repo),它应该将传递的参数作为 shell 命令执行,例如:

 foreach_repo hg status

应该执行命令hg status。(它在存储库的复杂嵌套结构上执行此操作,我无法控制此结构,但我需要经常对它们进行批量操作)。

这种类型的命令类似于sudo其他“高阶”命令;例如sudo hg status,将依次hg status以超级用户权限执行。

在内部,我的脚本执行以下操作(给定标准输入上的存储库路径提要,由脚本的另一部分创建 - 与此问题无关):

while read repo do
    container="$repo/.."
    cd $base/$container
    $@
done

Where$@旨在将传递的参数解释为要在存储库中执行的命令。

执行

这种方法适用于简单的命令,例如

foreach_repo hg status

将正确返回结构中每个存储库的状态列表。然而,更复杂的命令(带有转义符、引号......)被搞砸了。例如,当我尝试

foreach_repo hg commit -m "some message"

我明白了

abort: message: no such file or directory

因为引号被去掉了,实际执行的命令是:

hg commit -m some message

尝试的解决方案

手动转义引号或要传递的整个命令无效, using$*而不是$@. 但是,类似的命令sudo可以处理这种情况,即:

sudo hg commit -m "some message"

实际上(并且正确地)执行

hg commit -m "some message"

我怎样才能完成相同的行为?

4

1 回答 1

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
于 2013-08-20T10:49:12.563 回答