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 ."
工作完全符合原始提问者的要求。此技术有两个限制:
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我想要一个不局限于引用的关于这个答案的规范常见问题解答。我应该发布一个新问题吗?