下面是反引号和 $() 行为不同的示例:
$ echo "$(echo \"test\")"
"test"
$ echo "`echo \"test\"`"
test
我的理解是这是因为“反引号内的反斜杠 (\) 以不明显的方式处理”
但这似乎是另一回事,因为当我删除外部双引号时,结果变得相似:
$ echo $(echo \"test\")
"test"
$ echo `echo \"test\"`
"test"
有人可以解释一下它是如何工作的以及为什么 "`echo \"test\"`" 删除双引号吗?
答案1
你是对的,在这种情况下是另一回事。
解决方案依然在相同的链接,但第二点:
$() 内的嵌套引用要方便得多。
[...]
`...` 需要在内部引号周围使用反斜杠以便可移植。
因此,
echo "`echo \"test\"`"
不等于这个:
echo "$(echo \"test\")"
但是这个:
echo "$(echo "test")"
您需要将其与此进行比较:
echo "`echo \\"test\\"`"
答案2
使用反引号的命令替换会删除引号的原因是其实现方式造成的:
反引号内的文本在最终执行之前会再次通过词法解析器。
发生这种情况是因为第一次传递词法解析器(占用一个引用级别)的结果被保留为字符串,并在最终执行期间再次通过完整的解析器。因此,基于反引号的命令替换删除了两层引用。
在最终执行过程中,其中的文本
$(...)
在通过完整的解析器之前保持不变。这是
ksh
通过记录来实现的输入文本')̈́'
作为解析器在解析与. 匹配的结束语时的副作用$(
。这是通过记录解析器输出的二进制语法树
bosh
来实现的,同时解析器正在解析匹配的结束语并重建相关的等效项mksh
')̈́'
$(
原始输入为最终执行的二叉树。
因此,
$(...)
基于命令的替换仅删除单个引用级别(正常命令执行也会发生这种情况)。
由于这种实现,基于反引号的命令替换会导致意外行为,特别是如果grep
模式是此类命令的一部分。这就是为什么$(...)
建议优先使用。
答案3
原因是(双)引号字符串发生的情况。
一旦 shell 解析器找到一个(未加引号的),"
它就会声明以下一个(未加引号的)结尾的整个字符串"
被引用。在带引号的字符串内,最多反斜杠不被删除。在 UN 引用的字符串中,反斜杠被删除。
<backslash>
未引用的A应保留以下字符的字面值,...
这将在实践中显示(set -x 显示解析后 shell 执行的内容):
$ ksh -c 'set -x; echo test\h\jtest; set +x'
+ echo testhjtest
testhjtest
未加引号的反斜杠被删除。但引用的却没有:
$ ksh -c 'set -x; echo "test\h\jtest"; set +x'
+ echo 'test\h\jtest'
test\h\jtest
<backslash>
仅当后跟以下字符之一时,应保留其作为转义字符的特殊含义(请参阅转义字符(反斜杠))
这就是为什么反斜杠在带引号的字符串中被删除:
$ ` " \ <newline>
例子:
$ ksh -c 'set -x; echo "test\"\h\j\"test"; set +x'
+ echo 'test"\h\j"test'
test"\h\j"test
当找到的第一个字符是(双)引号时就会发生这种情况。
当找到的是初始反引号 ( `
) 时,解析规则会有所不同。首先,字符串以不带引号的方式开始(内部双引号可以开始带引号的部分),并且只有一引用范围(直到结束 ( )。请注意下面第一行的`
单引号:"
$ set -x; echo `echo \"\`echo \\"test\\" \`\"`; set +x
+++ echo '"test"'
++ echo '""test""'
+ echo '""test""'
""test""
+ set +x
$(…)
相反,A开始新的每次都引用范围。
$ set -x; echo $(echo \"$(echo \\"test\\" )\"); set +x
+++ echo '\test\'
++ echo '"\test\"'
+ echo '"\test\"'
"\test\"
+ set +x
结果完全不同。