为什么在bash中嵌套引号可以使用反引号?

5
更准确地说,为什么要这样做?
"`command "$variable"`"

将外层引号视为包含内部引号,而不是在引号外展开变量?

我用来测试的确切命令类似于另一个stackoverflow问题中提出的示例,该问题涉及在使用命令替换时引用的正确方法:

fileName="some path with/spaces"
echo "`dirname "$fileName"`"

正确地输出“some path with”,而不是因为参数数量无效而抱怨。

我阅读了Bash的man页面,在“EXPANSION”章节的“Commmand Substitution”部分中,它指出新式的$()替换保留括号内任何字符的含义,然而,关于反引号,它仅提到反斜杠的使用有限:

当使用旧式的反引号替换形式时,反斜杠保留其字面含义,除非后跟$、`或\。第一个未经过反斜杠转义的反引号终止命令替换。

我的第一个想法是反引号也是一样的,除了上述的例外,因此“引用”内部的双引号,但是我被告知这不是事实。 指引我朝这个方向的第二个观察是:

a=\$b
b=hello
echo `echo $a`

这段代码输出"$b"。如果反引号让美元符号被解释,第一个变量替换应该在子shell被调用之前发生,而子shell会扩展字符串"$b",结果是"hello."。 根据上述手册摘录,我甚至可以确保美元符号被引用,方法是使用

echo `echo \$a`

and the results would still be the same.
第三个观察结果让我有些怀疑:
echo `echo \\a`

结果:"\a"
echo \a

结果:a

在这里,似乎两个反斜杠在子shell出现之前都被保留了,尽管man页面说明在反引号内的反斜杠在后面跟着另一个反斜杠时不具有其字面意义。 编辑:^在这方面一切都按预期工作,我一定是使用了错误的shell(在我的其他终端中使用了tcsh,并使用了与“a”不同的字符)。

尽管我还没有找到实际发生了什么,但在寻找答案时,我遇到了一些人提到了与命令替换相关的“引用上下文”术语,但没有解释它是什么或在哪里描述它。 我在Bash参考资料(gnu.org、tldp、man bash)或通过DuckDuckGo没有找到任何关于“引用上下文”的真正参考。

除了知道正在发生什么之外,我更希望有一些参考或指导,以便从中区分出这种行为,因为我认为我可能没有把一些碎片放在一起,这自然而然地发生了。否则我会忘记答案。

对于那些建议人们使用新式美元符号和括号替换的人:在约50年历史的Unix机器上,有数十个或数百个不同的专有环境(不能为新版本的shell扔掉一个旧的),当人们必须编写兼容大多数可能使用的shell的脚本时,这不是一个选项。

感谢任何能帮助我的人。


4
你能否澄清这句话的意思:"将外部引号视为包含单引号,而不是在任何引号之外展开变量?"这个页面http://mywiki.wooledge.org/BashFAQ/082可能有助于理解反引号的行为方式。例如,在反引号中处理反斜杠的方式并不明显。 - calico_
1
你对 a=$b 的总结是错误的。解释不是递归的,因此当你回显 $a 时,你会得到分配给 a 的字符串,这恰好是 '$b',所以它与写成 a='$b' 是相同的。你可以继续使用 eval,eval echo $(echo $a)。 - grail
3
简单来说,命令替换会开始一个新的引号上下文。命令替换的内容将独立于它所在的引号进行处理。 - chepner
calico_: 感谢您指出,我错误地写成了“single”而不是“inner”。 - Larry
chepner:你能指引我一些更详细描述引用上下文或这种行为的参考资料吗?正如我在帖子中所述,当我寻找答案时,我已经遇到了这个术语,但没有任何解释。最好是我知道它如何适用于Bash手册页面描述的所有内容。 - Larry
calico_:你提供的链接并没有什么令人惊讶的内容,也没有回答问题的任何内容。它还说了一句话,我认为是错误的,即在反引号中嵌套双引号在Korn shell中不可能,尽管我正在使用Korn shell,并且使用“dirname”示例似乎可以正常工作。也许作者的意思是“一些”Korn shell,而不是“所有”。然而,与“$()”结合使用,它确实包含了一些关于引用上下文的更多解释,尽管它没有提到它也适用于反引号。 - Larry
1个回答

4

POSIX在2.2.3中指出(强调是我的):

` (反引号)

反引号应保留其特殊含义,引入命令替换的另一种形式(详见命令替换)。从初始反引号到下一个未经转义的反引号之间的引用字符串部分和字符,去除转义字符,定义了该命令,当单词扩展时,该命令的输出将替换“`...`”。以下任一情况都会产生未定义的结果:

  • 以“`...`”序列开头但未结束的单引号或双引号字符串

  • 以相同双引号字符串开始但未结束的“`...`”序列

对我来说,这几乎定义了其他人可能(非正式地?)称为两个连续反引号内的引用上下文。

在某种程度上,反引号是除单引号、双引号和反斜杠之外的第四个引号。请注意,在双引号内,单引号也失去了其引用功能,因此反引号改变了其中双引号的功能,这并不奇怪。
我尝试了您的示例,并使用了其他shell,例如FreeBSD上的Almquist shell和zsh。正如预期的那样,它们输出了some path with

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接