为啥 shell 忽略通过变量传递给它的参数中的引号字符? [复制]
Posted
技术标签:
【中文标题】为啥 shell 忽略通过变量传递给它的参数中的引号字符? [复制]【英文标题】:Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]为什么 shell 忽略通过变量传递给它的参数中的引号字符? [复制] 【发布时间】:2012-08-21 15:27:23 【问题描述】:这些工作如宣传的那样:
grep -ir 'hello world' .
grep -ir hello\ world .
这些不:
argumentString1="-ir 'hello world'"
argumentString2="-ir hello\\ world"
grep $argumentString1 .
grep $argumentString2 .
尽管'hello world'
在第二个示例中被引号括起来,grep 将'hello
(和hello\
)解释为一个参数,将world'
(和world
)解释为另一个参数,这意味着,在这种情况下, 'hello
将是搜索模式,world'
将是搜索路径。
同样,这只发生在从argumentString
变量扩展参数时。在第一个示例中,grep 正确地将 'hello world'
(和 hello\ world
)解释为单个参数。
谁能解释这是为什么?是否有适当的方法来扩展字符串变量,以保留每个字符的语法,以便 shell 命令正确解释它?
【问题讨论】:
我应该注意这与 grep 本身没有任何关系;这更像是一个 bash 问题(使用任何其他命令具有相同的效果) 【参考方案1】:当您将引号字符放入变量时,它们只是变成普通文字(参见http://mywiki.wooledge.org/BashFAQ/050;感谢@tripleee 指出此链接)
请尝试使用数组来传递参数:
argumentString=(-ir 'hello world')
grep "$argumentString[@]" .
【讨论】:
【参考方案2】:为什么
当字符串被扩展时,它被分割成单词,但它不会被重新评估以找到特殊字符,例如引号或美元符号或......这就是 shell 一直以来的行为方式,因为Bourne shell 早在 1978 年左右。
修复
在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 分词和 3.5.9 引号删除。
【讨论】:
或者,“不要那样做”。 mywiki.wooledge.org/BashFAQ/050 如果我们要用eval
来展示,不妨用它来展示。我认为使用eval
right always 包括将其传递给单个字符串-否则,您会将其所有参数与空格连接在一起,并且会以令人惊讶的方式变得混乱。考虑eval printf '%s\n' "hello world"
,与eval 'printf "%s\n" "hello world"'
相比,这是一个很好的例子,说明为什么传递eval
多个参数会导致混淆。
为什么grep "$argumentArray[@]" .
中的引号不会导致数组作为单个参数传递?我本来希望它是grep $argumentArray[@] .
,但似乎两者都可以在实践中使用?
@natevw:与"$@"
生成参数列表而不是单个字符串的原因大致相同("$*"
生成单个字符串,"$array[*]"
也是如此)。为什么"$@"
会这样做?因为自古以来就是这样(或者,至少是 Unix 的第 7 版,大约在 1978 年,它引入了 Bourne shell)。
@JonathanLeffler 我想要一个不限于引号的此答案的规范常见问题解答。我应该发布一个新问题吗?【参考方案3】:
在查看此问题和相关问题时,我很惊讶没有人提出使用显式子外壳。对于 bash 和其他现代 shell,您可以显式执行命令行。在 bash 中,它需要 -c 选项。
argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
完全按照原始提问者的要求工作。这种技术有两个限制:
-
您只能在命令或参数字符串中使用单引号。
只有导出的环境变量可用于命令
此外,此技术处理重定向和管道,其他 shellisms 也可以工作。您还可以使用 bash 内部命令以及在命令行上工作的任何其他命令,因为您实际上是在要求 subshell bash 将其直接解释为命令行。这是一个更复杂的例子,一个有点复杂的 ls -l 变体。
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"
我已经用这种方式和参数数组构建了命令处理器。通常,这种方式更更容易编写和调试,并且回显您正在执行的命令也很简单。 OTOH,当您确实有抽象的参数数组时,参数数组可以很好地工作,而不是只想要一个简单的命令变体。
【讨论】:
以上是关于为啥 shell 忽略通过变量传递给它的参数中的引号字符? [复制]的主要内容,如果未能解决你的问题,请参考以下文章