Bash 为什么shell忽略通过变量传递给它的参数中的引号字符?

Bash 为什么shell忽略通过变量传递给它的参数中的引号字符?,bash,variables,syntax,quoting,expansion,Bash,Variables,Syntax,Quoting,Expansion,这些工作如广告所示: grep -ir 'hello world' . grep -ir hello\ world . 这些没有: argumentString1="-ir 'hello world'" argumentString2="-ir hello\\ world" grep $argumentString1 . grep $argumentString2 . 尽管在第二个示例中,'hello world'被引号括起来,但grep将'hello

这些工作如广告所示:

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命令正确解释?

当您将引号字符放入变量时,它们只会变成纯文本(请参阅;感谢@tripleee指出此链接)

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

argumentString=(-ir 'hello world')
grep "${argumentString[@]}" .
argumentArray=(-ir 'hello world')
grep "${argumentArray[@]}" .
为什么? 当字符串展开时,它被拆分为单词,但不会重新计算以查找特殊字符,如引号或美元符号或。。。自1978年左右伯恩壳牌公司成立以来,这就是壳牌公司“一直”的行为方式

修理 在
bash
中,使用数组保存参数:

argumentString=(-ir 'hello world')
grep "${argumentString[@]}" .
argumentArray=(-ir 'hello world')
grep "${argumentArray[@]}" .
或者,如果勇敢/鲁莽,使用
eval

argumentString="-ir 'hello world'"
eval "grep $argumentString ."
另一方面,谨慎往往是英勇的更好部分,而与
eval
合作是谨慎胜于勇敢的地方。如果您不能完全控制
eval
'd字符串(如果命令字符串中有任何未经严格验证的用户输入),那么您将面临潜在的严重问题


注意,GNUBASH手册中描述了Bash的扩展顺序。请注意,特别是在第3.5.3节Shell参数扩展、第3.5.7节分词和第3.5.9节引号删除中。

在查看此问题和相关问题时,我很惊讶没有人使用显式子Shell。对于bash和其他现代shell,可以显式执行命令行。在bash中,它需要-c选项

argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
工作完全符合原始提问者的要求。此技术有两个限制:

  • 只能在命令或参数字符串中使用单引号
  • 命令只能使用导出的环境变量
  • 此外,该技术还可以处理重定向和管道,以及其他Shellism。您还可以使用bash内部命令以及在命令行工作的任何其他命令,因为您本质上要求subshell bash将其直接解释为命令行。这里有一个更复杂的例子,一个稍微复杂的ls-l变体

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

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

    我应该注意,这与grep本身没有任何关系;这更像是一个bash问题(使用任何其他命令都有相同的效果)或“不要这样做”。如果我们要使用
    eval
    进行展示,那么最好正确地使用它进行展示。我认为使用
    eval
    right总是包含传递一个字符串的过程——否则,您会将它的所有参数与空格连接在一起,这会以令人惊讶的方式变得混乱。考虑<代码> EvravePrtuf'%s\n“hello World”< /> >,与<代码> EVAL Prrtf“%s\n”“Hello World”相比,对于一个“为什么通过<代码> EVA/COD> >的多个例子的例子,会导致混乱。为什么不引用<代码> GRIP”${AgMuthTrime[a] }?< /Cord>使数组作为单个参数传递?我本来希望它是
    grep${argumentArray[@]}。
    ,但这两种方法在实践中似乎都有效?@natevw:原因大致相同,
    “$@”
    产生的参数列表不是单个字符串(并且
    “$*”
    产生的是单个字符串,就像
    “${array[*}”
    )。为什么
    “$@”
    会这样做?因为从远古时代(或者至少是1978年左右的第7版Unix,它引入了Bourne shell)就一直如此。@JonathanLeffler我想要一个不局限于引用的关于这个答案的规范常见问题解答。我应该发布一个新问题吗?