难以理解反引号内反斜杠的非明显使用
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)
替换
- 运行命令
以下是评估 `string`
命令替换的方法:
- 确定字符串的跨度,从开始反引号到结束未转义反引号(如果此反引号在字符串文字内,则行为未定义:shell 通常将其视为文字反引号或作为取决于其解析器实现的结束反引号)
- 通过删除美元、反引号或反斜杠三个字符之一之前的反斜杠来取消转义字符串。随后的字符将按字面意义插入到命令中。反斜杠后跟任何其他字符将被保留。
- 例如
Hello\ World
将变为 Hello\ World
,因为 \
被替换为 \
Hello\ World
也将变为 Hello\ World
,因为反斜杠后跟的字符不是这三个字符之一,因此保留其字面意思只是一个反斜杠
\\*
将变为 \*
,因为 \
将变为 \
(因为反斜杠是三个之一),而 \*
将保持 \*
(因为星号不是)
- 将结果作为 shell 命令进行评估(这包括对现在未转义的命令字符串的结果遵循所有常规 shell 转义规则)
所以要评估echo `echo \*`
:
- 确定字符串的跨度,这里
echo \*
- 根据反引号规则进行反转义:
echo \*
- 将其作为命令进行评估,运行 echo 以输出文字
*
- 由于替换的结果未被引用,输出将经历:
- 分词:
*
变成 *
(因为它只是一个词)
- 每个单词的路径名扩展,因此
*
变为 bin
Desktop
Downloads
Photos
public_html
当前目录
- 请特别注意,这与用输出替换反引号命令并重新运行结果不同。例如,我们没有考虑输出中的转义、引号和扩展,这是一个简单的基于文本的宏扩展。
- 将这些中的每一个作为参数传递给下一个命令(也回显):
echo bin Desktop Downloads Photos public_html
结果是当前目录中的文件列表。
我已经阅读了大量页面,包括 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)
替换
- 运行命令
以下是评估 `string`
命令替换的方法:
- 确定字符串的跨度,从开始反引号到结束未转义反引号(如果此反引号在字符串文字内,则行为未定义:shell 通常将其视为文字反引号或作为取决于其解析器实现的结束反引号)
- 通过删除美元、反引号或反斜杠三个字符之一之前的反斜杠来取消转义字符串。随后的字符将按字面意义插入到命令中。反斜杠后跟任何其他字符将被保留。
- 例如
Hello\ World
将变为Hello\ World
,因为\
被替换为\
Hello\ World
也将变为Hello\ World
,因为反斜杠后跟的字符不是这三个字符之一,因此保留其字面意思只是一个反斜杠\\*
将变为\*
,因为\
将变为\
(因为反斜杠是三个之一),而\*
将保持\*
(因为星号不是)
- 例如
- 将结果作为 shell 命令进行评估(这包括对现在未转义的命令字符串的结果遵循所有常规 shell 转义规则)
所以要评估echo `echo \*`
:
- 确定字符串的跨度,这里
echo \*
- 根据反引号规则进行反转义:
echo \*
- 将其作为命令进行评估,运行 echo 以输出文字
*
- 由于替换的结果未被引用,输出将经历:
- 分词:
*
变成*
(因为它只是一个词) - 每个单词的路径名扩展,因此
*
变为bin
Desktop
Downloads
Photos
public_html
当前目录 - 请特别注意,这与用输出替换反引号命令并重新运行结果不同。例如,我们没有考虑输出中的转义、引号和扩展,这是一个简单的基于文本的宏扩展。
- 分词:
- 将这些中的每一个作为参数传递给下一个命令(也回显):
echo bin Desktop Downloads Photos public_html
结果是当前目录中的文件列表。