命令替换和双引号:为什么结果不同?

命令替换和双引号:为什么结果不同?

下面是反引号和 $() 行为不同的示例:

$ 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 引用的字符串中,反斜杠被删除。

POSIX(删除大部分反斜杠)

<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

POSIX 双引号

<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

结果完全不同。

相关内容