在bash中,当参数作为variables传递给它们时,为什么shell命令在参数中忽略引号?
这工作如广告:
# example 1 #!/bin/bash grep -ir 'hello world' .
这不:
# example 2 #!/bin/bash argumentString="-ir 'hello world'" grep $argumentString .
尽pipe在第二个例子中引用了'hello world'
,但是grep将'hello
as one argument and world'
为另一个,这意味着在这种情况下, 'hello
将是search模式, world'
将成为searchpath。
同样,只有当参数从argumentString
variables扩展时才会发生这种情况。 在第一个例子中,grep正确地将'hello world'
为单个参数。
任何人都可以解释为什么这是? 有没有适当的方法来扩展一个stringvariables,将保留每个字符的语法,使其正确解释的shell命令?
为什么
当string被扩展时,它被分割成单词,但是它不会被重新评估以find特殊字符,例如引号或美元符号或…这就是shell自始至终performance出来的方式,因为Bourne shell返回在1978年左右。
固定
在bash
,使用一个数组来保存参数:
argumentArray=("-ir" "hello world") grep "${argumentArray[@]}" .
或者,如果勇敢/蛮干,请使用eval
:
argumentString="-ir 'hello world'" eval grep $argumentString .
另一方面,谨慎往往是勇气的更好的一部分,与eval
一起工作是一个谨慎胜于勇敢的地方。 如果您不完全控制eval
的string(如果在命令string中有任何用户input未经过严格validation),那么您将面临潜在的严重问题。
请注意,Bash的扩展序列在GNU Bash手册的Shell Expansions中有描述。 请特别注意章节3.5.3shell参数扩展,3.5.7分词和3.5.9报价清除。
当把引号字符放入variables中时,它们只是简单的文字(参见http://mywiki.wooledge.org/BashFAQ/050 ;感谢@tripleee指出这个链接)
相反,尝试使用数组来传递你的参数:
argumentString=(-ir 'hello world') grep "${argumentString[@]}" .
在双引号内,单引号失去了特殊的含义,所以它们只是普通的字符。 为了工作,使用像eval
这样的东西:
eval grep $argumentString .
这应该从串联的string中正确地重新组装命令行。
在看这个和相关的问题,我很惊讶,没有人提出使用明确的子shell。 对于bash和其他现代的shell,你可以明确地执行命令行。 在bash中,它需要-c选项。
argumentString="-ir 'hello world'" bash -c "grep $argumentString ."
与所需的原始提问者完全一致。 这种技术有两个限制:
- 您只能在命令或参数string中使用单引号。
- 只有导出的环境variables可用于该命令
此外,这种技术处理redirect和pipe道,其他shellisms也起作用。 你也可以使用bash内部命令以及命令行中的任何其他命令,因为实质上是要求子shell bash将其直接解释为命令行。 这是一个更复杂的例子,一个有点无理复杂的ls -l变体。
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'" bash -c "$cmd"
我已经用这种方法和参数数组构build了命令处理器。 一般来说,这种方式编写和debugging起来要容易得多 ,而且回显你正在执行的命令也是微不足道的。 OTOH,当你确实拥有抽象的参数数组时,param数组可以很好地工作,而不是仅仅需要一个简单的命令variables。