为什么shell忽略通过变量传递给它的参数中的引号? [重复]

问题描述 投票:32回答:3

这个问题在这里已有答案:

这与宣传的一样:

# example 1
#!/bin/bash
grep -ir 'hello world' .

这不是:

# example 2
#!/bin/bash
argumentString="-ir 'hello world'"
grep $argumentString .

尽管'hello world'被第二个例子中的引号括起来,但grep将'hello解释为一个参数而world'解释为另一个,这意味着,在这种情况下,'hello将是搜索模式,world'将成为搜索路径。

同样,这只发生在从argumentString变量扩展参数时。在第一个例子中,grep正确地将'hello world'解释为单个参数。

谁能解释为什么会这样?有没有一种正确的方法来扩展一个字符串变量,它将保留每个字符的语法,以便shell命令正确解释它?

bash variables syntax quoting expansion
3个回答
29
投票

Why

当字符串被展开时,它被分成单词,但不会重新评估它以找到特殊字符,如引号或美元符号或......这就是shell“始终”表现的方式,因为Bourne shell返回在1978年左右。

Fix

bash中,使用数组来保存参数:

argumentArray=(-ir 'hello world')
grep "${argumentArray[@]}" .

或者,如果勇敢/愚蠢,使用eval

argumentString="-ir 'hello world'"
eval "grep $argumentString ."

另一方面,自由裁量权往往是勇敢的更好的部分,与eval合作是一个自由裁量权比勇敢更好的地方。如果您没有完全控制eval'd的字符串(如果命令字符串中的任何用户输入未经过严格验证),那么您将面临潜在的严重问题。

请注意,Bash的扩展序列在GNU Bash手册中的Shell Expansions中有所描述。请特别注意3.5.3 Shell参数扩展,3.5.7 Word拆分和3.5.9报价删除。


5
投票

当您将引号字符放入变量时,它们只会变成普通文字(请参阅http://mywiki.wooledge.org/BashFAQ/050;感谢@tripleee指出此链接)

相反,尝试使用数组传递您的参数:

argumentString=(-ir 'hello world')
grep "${argumentString[@]}" .

1
投票

在查看这个问题和相关问题时,我很惊讶没有人使用明确的子shell。对于bash和其他现代shell,您可以显式执行命令行。在bash中,它需要-c选项。

argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."

完全像原始提问者一样工作。这种技术有两个限制:

  1. 您只能在命令或参数字符串中使用单引号。
  2. 只有导出的环境变量才可用于该命令

此外,这种技术处理重定向和管道,其他shellism也工作。您还可以使用bash内部命令以及在命令行中运行的任何其他命令,因为您实际上是要求子shell bash将其直接解释为命令行。这是一个更复杂的例子,一个有点无偿复杂的ls -l变体。

cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"

我已经用这种方式和参数数组构建了命令处理器。通常,这种方式更容易编写和调试,并且回显您正在执行的命令是微不足道的。 OTOH,当您确实拥有抽象的参数数组时,param数组可以很好地工作,而不是只想要一个简单的命令变体。

© www.soinside.com 2019 - 2024. All rights reserved.