【问题标题】:Get exact quoted string parameters in Bash在 Bash 中获取精确的带引号的字符串参数
【发布时间】:2012-06-07 21:12:04
【问题描述】:

问题

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

场景

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

./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 proposed 这样的引号时,变量$* 包含正确引用的字符串,例如:

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

然后在wrap.sh代码中:

echo $*

会产生

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

但是,执行$* 会产生与上述完全相同的错误:formula 的参数在“AG”之后被剥离。此外,最好不要转义引号。

可以使用不同的 shell 吗?

【问题讨论】:

  • 这很奇怪。 $# 也给出 1,这意味着 $* 确实是一个令牌并且没有被分解。但不知何故,当$* 扩展时,它会扩展为许多令牌。
  • $* 不会按照您的想法进行标记。 “$@”(带有引号)通常更好。

标签: bash sh


【解决方案1】:

请参阅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).

【讨论】:

    【解决方案2】:

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

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

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

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

    输出:

    0: ./tool
    1: --formula=Foo bar baz
    

    【讨论】:

    • 请注意./wrap.sh ./tool --formula='"Foo bar baz"' 也可以。
    【解决方案3】:

    您应该使用\ 转义"。但是,要将它们保存在一个字符串中,您应该再次将它们括在" 中。那就是:

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

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

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

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

    注意每个" 现在如何变成\",每个\ 变成\\。整个内容都用引号括起来。

    附:您可以使用 echo 进行测试:

    $ echo "a b c"
    a b c
    $ echo "\"a b c\""
    "a b c"
    $ echo "echo \"\\\"a b c\\\"\""
    echo "\"a b c\""
    

    【讨论】:

    • 这解决了部分问题:当我 echo $* 打印正确的参数(带引号)时。但是当我执行$* 时,同样的问题出现了。
    • @NielsLohmann,这样想。 bash首先分析输入,去除特殊字符,然后你再把它交给bash,所以重新分析和去除特殊字符。所以你需要逃避一切,这样在第一次分析之后,你就会得到我提到的那条线。我将编辑我的答案。
    • 感谢您的编辑,但它仍然没有达到应有的效果。我编辑了问题并提供了一些示例。
    猜你喜欢
    • 2012-12-17
    • 1970-01-01
    • 2014-04-13
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    相关资源
    最近更新 更多