Ben*_*elm 33 variables syntax bash quoting expansion
这与宣传的一样:
# example 1
#!/bin/bash
grep -ir 'hello world' .
Run Code Online (Sandbox Code Playgroud)
这不是:
# example 2
#!/bin/bash
argumentString="-ir 'hello world'"
grep $argumentString .
Run Code Online (Sandbox Code Playgroud)
尽管'hello world'在第二个例子中被引号括起来,但grep解释'hello为一个参数和world'另一个参数,这意味着,在这种情况下,'hello将是搜索模式world'并将成为搜索路径.
同样,这仅在从argumentString变量扩展参数时发生.grep 'hello world'在第一个示例中正确解释为单个参数.
谁能解释为什么会这样?有没有一种正确的方法来扩展一个字符串变量,它将保留每个字符的语法,以便shell命令正确解释它?
Jon*_*ler 32
当字符串被展开时,它被分成单词,但不会重新评估它以找到特殊字符,如引号或美元符号或......这就是shell"始终"表现的方式,因为Bourne shell返回在1978年左右.
在bash,使用数组来保存参数:
argumentArray=(-ir 'hello world')
grep "${argumentArray[@]}" .
Run Code Online (Sandbox Code Playgroud)
或者,如果勇敢/蛮干,请使用eval:
argumentString="-ir 'hello world'"
eval "grep $argumentString ."
Run Code Online (Sandbox Code Playgroud)
另一方面,自由裁量权通常是勇敢的一部分,而与之合作的eval是一个自由裁量权优于勇敢的地方.如果您没有完全控制eval'd 的字符串(如果命令字符串中的任何用户输入未经过严格验证),那么您将面临潜在的严重问题.
请注意,Bash的扩展序列在GNU Bash手册中的Shell Expansions中有所描述.请特别注意3.5.3 Shell参数扩展,3.5.7 Word拆分和3.5.9报价删除.
当您将引号字符放入变量时,它们只会变成普通文字(请参阅http://mywiki.wooledge.org/BashFAQ/050 ;感谢@tripleee指出此链接)
相反,尝试使用数组传递您的参数:
argumentString=(-ir 'hello world')
grep "${argumentString[@]}" .
Run Code Online (Sandbox Code Playgroud)
在查看这个和相关问题时,我很惊讶没有人提出使用显式子shell。对于 bash 和其他现代 shell,您可以显式执行命令行。在 bash 中,它需要 -c 选项。
argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
Run Code Online (Sandbox Code Playgroud)
完全按照原始提问者的要求工作。这种技术有两个限制:
此外,这种技术处理重定向和管道,其他 shellisms 也能工作。您还可以使用 bash 内部命令以及在命令行中工作的任何其他命令,因为您实际上是在要求子 shell bash 将其直接解释为命令行。这是一个更复杂的例子,一个有点复杂的 ls -l 变体。
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"
Run Code Online (Sandbox Code Playgroud)
我已经以这种方式和参数数组构建了命令处理器。通常,这种方式更容易编写和调试,并且回显您正在执行的命令很简单。OTOH,当您确实拥有抽象的参数数组时,param 数组可以很好地工作,而不仅仅是想要一个简单的命令变体。