bash 为什么 shell 忽略通过变量传递给它的参数中的引号?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12136948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does shell ignore quotes in arguments passed to it through variables?
提问by Ben Wilhelm
This works as advertised:
这就像宣传的那样:
# example 1
#!/bin/bash
grep -ir 'hello world' .
This doesn't:
这不会:
# example 2
#!/bin/bash
argumentString="-ir 'hello world'"
grep $argumentString .
Despite 'hello world'being enclosed by quotes in the second example, grep interprets 'helloas one argument and world'as another, which means that, in this case, 'hellowill be the search pattern and world'will be the search path.
尽管'hello world'在第二个示例中被引号括起来,grep 将解释'hello为一个参数和world'另一个参数,这意味着,在这种情况下,'hello将是搜索模式和world'搜索路径。
Again, this only happens when the arguments are expanded from the argumentStringvariable. grep properly interprets 'hello world'as a single argument in the first example.
同样,这仅在从argumentString变量扩展参数时发生。grep'hello world'在第一个示例中正确解释为单个参数。
Can anyone explain why this is? Is there a proper way to expand a string variable that will preserve the syntax of each character such that it is correctly interpreted by shell commands?
谁能解释这是为什么?是否有适当的方法来扩展字符串变量,以保留每个字符的语法,以便 shell 命令正确解释它?
采纳答案by Jonathan Leffler
Why
为什么
When the string is expanded, it is split into words, but it is not re-evaluated to find special characters such as quotes or dollar signs or ... This is the way the shell has 'always' behaved, since the Bourne shell back in 1978 or thereabouts.
当字符串被扩展时,它被拆分成单词,但不会重新评估以找到特殊字符,例如引号或美元符号或......这是 shell '始终' 的行为方式,因为 Bourne shell 返回1978 年左右。
Fix
使固定
In bash, use an array to hold the arguments:
在 中bash,使用数组来保存参数:
argumentArray=(-ir 'hello world')
grep "${argumentArray[@]}" .
Or, if brave/foolhardy, use eval:
或者,如果勇敢/粗鲁,请使用eval:
argumentString="-ir 'hello world'"
eval "grep $argumentString ."
On the other hand, discretion is often the better part of valour, and working with evalis a place where discretion is better than bravery. If you are not completely in control of the string that is eval'd (if there's any user input in the command string that has not been rigorously validated), then you are opening yourself to potentially serious problems.
另一方面,谨慎往往是勇气中更好的部分,在工作eval中谨慎胜于勇敢。如果您不能完全控制eval'd的字符串(如果命令字符串中有任何未经严格验证的用户输入),那么您将面临潜在的严重问题。
Note that the sequence of expansions for Bash is described in Shell Expansionsin the GNU Bash manual. Note in particular sections 3.5.3 Shell Parameter Expansion, 3.5.7 Word Splitting, and 3.5.9 Quote Removal.
请注意,Bash 的扩展序列在 GNU Bash 手册中的Shell Expansions中进行了描述。请特别注意 3.5.3 Shell 参数扩展、3.5.7 Word Splitting 和 3.5.9 Quote Removal。
回答by nneonneo
When you put quote characters into variables, they just become plain literals (see http://mywiki.wooledge.org/BashFAQ/050; thanks @tripleee for pointing out this link)
当您将引号字符放入变量时,它们就变成了普通文字(请参阅http://mywiki.wooledge.org/BashFAQ/050;感谢 @tripleee 指出此链接)
Instead, try using an array to pass your arguments:
相反,尝试使用数组来传递参数:
argumentString=(-ir 'hello world')
grep "${argumentString[@]}" .
回答by markgo2k
In looking at this and related questions, I'm surprised that no one brought up using an explicit subshell. For bash, and other modern shells, you can execute a command line explicitly. In bash, it requires the -c option.
在查看这个和相关问题时,我很惊讶没有人提出使用显式子shell。对于 bash 和其他现代 shell,您可以显式执行命令行。在 bash 中,它需要 -c 选项。
argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
Works exactly as original questioner desired. There are two restrictions to this technique:
完全按照原始提问者的要求工作。这种技术有两个限制:
- You can only use single quotes within the command or argument strings.
- Only exported environment variables will be available to the command
- 您只能在命令或参数字符串中使用单引号。
- 只有导出的环境变量可用于命令
Also, this technique handles redirection and piping, and other shellisms work as well. You also can use bash internal commands as well as any other command that works at the command line, because you are essentially asking a subshell bash to interpret it directly as a command line. Here's a more complex example, a somewhat gratuitously complex ls -l variant.
此外,这种技术处理重定向和管道,其他 shellisms 也能工作。您还可以使用 bash 内部命令以及在命令行中工作的任何其他命令,因为您实际上是在要求子 shell bash 将其直接解释为命令行。这是一个更复杂的例子,一个有点复杂的 ls -l 变体。
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"
I have built command processors both this way and with parameter arrays. Generally, this way is mucheasier to write and debug, and it's trivial to echo the command you are executing. OTOH, param arrays work nicely when you really do have abstract arrays of parameters, as opposed to just wanting a simple command variant.
我已经以这种方式和参数数组构建了命令处理器。通常,这种方式更容易编写和调试,并且回显您正在执行的命令很简单。OTOH,当您确实拥有抽象的参数数组时,param 数组可以很好地工作,而不仅仅是想要一个简单的命令变体。

