4

问题

我想按原样使用给脚本的参数- 带引号和所有内容。到目前为止,我发现的所有解决方案都去掉了引号,或者做一些微妙的不同。

设想

例如,我有一个接受字符串作为命令行参数的工具

./tool --formula="AG EF phi;"

我还有一个应该调用脚本的包装脚本。它的调用应该是

wrap.sh ./tool --formula="AG EF phi;"

这个脚本做了类似的事情

# preparation phase
# ...

# call tool and remember exit code
$*
result=$?

# evaluation phase
# ...

exit $result

不幸的是,该工具随后被称为

./tool --formula=AG EF phi;

这与上面不同,会产生语法错误。我发现的唯一黑客会给我类似的东西

./tool "--formula=AG EF phi;"

这也是错误的。

细节

具体来说,这里有一些虚拟代码来举例说明这个问题:

首先,C程序“tool.c”。

#include <stdio.h>

int main(int argc, char** argv) {
    int i;
    for (i = 0; i < argc; i++) {
        printf("%d: %s\n", i, argv[i]);
    }
    return 0;
}

然后,包装脚本“wrap.sh”:

#!/bin/bash
echo $*
$*
exit $?

现在这是我尝试过的:

$ ./tool --formula="Foo bar"
0: ./tool
1: --formula=Foo bar

$ ./wrap.sh ./tool --formula="Foo bar"
./tool --formula=Foo bar
0: ./tool
1: --formula=Foo
2: bar

$ ./wrap.sh ./tool --formula="\"Foo bar\""
./tool --formula="Foo bar"
0: ./tool
1: --formula="Foo
2: bar"

$ ./wrap.sh ./tool --formula="\"Foo bar\""
./tool --formula="Foo bar"
0: ./tool
1: --formula="Foo
2: bar"

$ ./wrap.sh "./tool --formula=\"\\\"Foo bar\\\"\""
./tool --formula="\"Foo bar\""
0: ./tool
1: --formula="\"Foo
2: bar\""

这就是我想要的(对于包装脚本中的任何必要更改):

$ ./wrap.sh ./tool --formula="Foo bar"
0: ./tool
1: --formula=Foo bar

提案

转义行情

当我转义像Shabaz 提议的引号时,变量$*包含正确引用的字符串,例如:

wrap.sh ./tool --formula="\"AG EF phi;\""

然后在wrap.sh代码中:

echo $*

会产生

./tool --formula="AG EF phi;"

但是,执行$*会产生如上的确切错误:参数 offormula在“AG”之后被剥离。此外,最好不要转义引号。

不同的外壳有帮助吗?

4

3 回答 3

4

请参阅BashFAQ/050(“我正在尝试将命令放入变量中,但复杂的情况总是失败!”)。

为了保留空格并正确处理参数传递,您应该使用它来执行包装脚本的参数:

"$@"

使用此方法,您无需在运行包装器时进行任何额外的引用。

$ cat ./wrap.sh
#!/bin/bash
echo "$@"
"$@"
result=$?
exit $result
$ ./wrap.sh ./tool --formula="Foo bar"
./tool --formula=Foo bar
0: ./tool
1: --formula=Foo bar

使用$@或不加$*引号或加引号$*会使字符串变平,以便将其视为丢失所需分组的单词序列。

来自man bash

   *      Expands  to  the positional parameters, starting from one.  When
          the expansion occurs within double quotes, it expands to a  sin‐
          gle word with the value of each parameter separated by the first
          character of the IFS special variable.  That is, "$*" is equiva‐
          lent to "$1c$2c...", where c is the first character of the value
          of the IFS variable.  If IFS is unset, the parameters are  sepa‐
          rated  by  spaces.   If  IFS  is null, the parameters are joined
          without intervening separators.
   @      Expands to the positional parameters, starting from  one.   When
          the  expansion  occurs  within  double  quotes,  each  parameter
          expands to a separate word.  That is, "$@" is equivalent to "$1"
          "$2"  ...   If the double-quoted expansion occurs within a word,
          the expansion of the first parameter is joined with  the  begin‐
          ning  part  of  the original word, and the expansion of the last
          parameter is joined with the last part  of  the  original  word.
          When  there  are no positional parameters, "$@" and $@ expand to
          nothing (i.e., they are removed).
于 2012-06-07T11:16:53.757 回答
2

您可以使用此脚本来保留所需的空格:

#!/bin/bash
exec bash -c "$*"
ret=$?
exit $ret

使用此命令行执行上述脚本:

./wrap.sh ./tool --formula="\"Foo bar baz\""

输出:

0: ./tool
1: --formula=Foo bar baz
于 2012-06-07T11:21:47.483 回答
1

你应该"\. 但是,要将它们保存在一个字符串中,您应该再次将它们括在". 那是:

./tool --formula="\"AG EF phi;\""

Bash 会自动将 any 转换"text"text一个单词,即使它包含空格。它像任何其他非特殊字符一样威胁转义字符。因此,要实际编写"您需要使用的字符\"。这两点的组合("用于将空格分隔的单词保持为一个单词并需要输入"自身)导致了上面的行。

现在你想让你的脚本执行这一行,但是你将它作为 bash 本身的参数传递给脚本。因此,这种特殊字符的分析和替换/删除需要进行两次。因此,您需要以这样的方式将参数传递给脚本,以便在此分析之后,它将产生上面的行。所以:

./wrap.sh "./tool --formula=\"\\\"AG EF phi;\\\"\""

请注意 every"现在是如何变成\"和 every \into\\的。整件事都用引号括起来。

PS你可以用回声测试这个:

$ echo "a b c"
a b c
$ echo "\"a b c\""
"a b c"
$ echo "echo \"\\\"a b c\\\"\""
echo "\"a b c\""
于 2012-06-07T09:43:41.837 回答