难以理解反引号内反斜杠的非明显使用

Trouble understanding the non-obvious use of backslash inside of backticks

我已经阅读了大量页面,包括 bash 手册,但仍然发现 "non-obvious" 反斜杠的使用令人困惑。

如果我这样做:

echo \*

它打印一个星号,这是正常的,因为我正在转义星号使其成为文字。

如果我这样做:

echo \*

它打印 \* 这似乎也很正常,第一个反斜杠转义了第二个。

如果我这样做

echo `echo \*`

它打印目录的内容。但在我看来,它应该打印与 echo \* 相同的内容,因为当它被替换并传递给 echo 时。我知道这是每个人都在谈论的反斜杠的不明显使用,但我很难理解为什么会这样。

bash 手册也说

When the old-style backquote form of substitution is used, backslash retains its literal meaning except when followed by ‘$’, ‘`’, or ‘\’.

但它没有定义 "literal meaning on backslash" 是什么。它是作为转义字符、延续字符还是只是字面上的反斜杠字符?

另外,它说它保留了它的字面意思,除非后面跟着... 那么当它后面跟着这三个字符之一时,它会做什么?它只转义这三个字符吗?

这主要是出于历史兴趣,因为 `...` 命令替换已被更简洁的 $(...) 形式所取代。任何新脚本都不应使用反引号。

这是您如何评估 $(command) 替换

  1. 运行命令

以下是评估 `string` 命令替换的方法:

  1. 确定字符串的跨度,从开始反引号到结束未转义反引号(如果此反引号在字符串文字内,则行为未定义:shell 通常将其视为文字反引号或作为取决于其解析器实现的结束反引号)
  2. 通过删除美元、反引号或反斜杠三个字符之一之前的反斜杠来取消转义字符串。随后的字符将按字面意义插入到命令中。反斜杠后跟任何其他字符将被保留。
    • 例如Hello\ World 将变为 Hello\ World,因为 \ 被替换为 \
    • Hello\ World 也将变为 Hello\ World,因为反斜杠后跟的字符不是这三个字符之一,因此保留其字面意思只是一个反斜杠
    • \\* 将变为 \*,因为 \ 将变为 \(因为反斜杠是三个之一),而 \* 将保持 \*(因为星号不是)
  3. 将结果作为 shell 命令进行评估(这包括对现在未转义的命令字符串的结果遵循所有常规 shell 转义规则)

所以要评估echo `echo \*`

  1. 确定字符串的跨度,这里echo \*
  2. 根据反引号规则进行反转义:echo \*
  3. 将其作为命令进行评估,运行 echo 以输出文字 *
  4. 由于替换的结果未被引用,输出将经历:
    1. 分词:* 变成 *(因为它只是一个词)
    2. 每个单词的路径名扩展,因此 * 变为 bin Desktop Downloads Photos public_html当前目录
    3. 请特别注意,这与用输出替换反引号命令并重新运行结果不同。例如,我们没有考虑输出中的转义、引号和扩展,这是一个简单的基于文本的宏扩展。
  5. 将这些中的每一个作为参数传递给下一个命令(也回显):echo bin Desktop Downloads Photos public_html

结果是当前目录中的文件列表。