为什么shell忽略引用传递给变量的参数中的引号?

2024-05-23 19:37:45 发布

您现在位置:Python中文网/ 问答频道 /正文

它的工作原理与广告一致:

# 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命令能够正确地解释它?


Tags: bash示例helloworld参数binirexample
3条回答

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

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

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

为什么

当字符串被展开时,它会被拆分成单词,但不会重新计算以查找特殊字符,如引号或美元符号或。。。这是自1978年左右的伯恩壳牌(Bourne shell)以来,壳牌一直“一贯”的做法。

修复

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

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

或者,如果勇敢/鲁莽,使用eval

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

另一方面,谨慎往往是勇敢的最好部分,与eval合作是一个谨慎胜于勇敢的地方。如果您不能完全控制eval的字符串(如果命令字符串中有任何用户输入未经过严格验证),那么您将面临潜在的严重问题。

注意,GNU Bash手册中的Shell Expansions描述了Bash的扩展序列。请特别注意第3.5.3节Shell参数扩展、第3.5.7节单词拆分和第3.5.9节引号删除。

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

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

完全符合最初提问者的要求。这种技术有两个限制:

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

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

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

我用这种方法和参数数组构建了命令处理器。一般来说,这种方式编写和调试要容易得多,响应正在执行的命令也很简单。OTOH,param数组在确实有抽象的参数数组时工作得很好,而不是仅仅需要一个简单的命令变量。

相关问题 更多 >