Zsh 反斜杠疯狂?

Zsh backslash madness?

当您尝试 echo 一堆反斜杠时,Zsh 似乎会做一些奇怪的反斜杠。我似乎无法找出一个非常清晰的模式。这种疯狂有什么原因吗?当然,如果我真的想正确使用反斜杠,那么我会使用适当的引号等,但为什么首先会发生这种情况?

这里有一个小例子来展示相同的内容:

$ echo \
\
$ echo \ \
\ \
$ echo \ \ \
\ \ \
$ echo \ \ \ \
\ \ \ \
$ echo \\ \ \
\ \ \
$ echo \\\ \
\ \
$ echo \\\\
\

我最初是在不久前独立发现的,但 Zach Riggle this tweet 提醒了我。

第一步,echo命令并不特殊。命令行由独立于正在执行的命令的规则来解析。此步骤的整体效果是将您的命令从一系列字符转换为一系列单词。

要理解此示例,您需要了解的两个通用解析规则是:space 字符分隔单词,反斜杠字符转义特殊字符,包括它本身。

所以命令echo \变成了2个单词的列表:

echo
\

第一个反斜杠转义第二个反斜杠,导致第二个单词中有一个反斜杠。

echo \ \ \ \

变成这个单词列表:

echo
\
\
\
\

现在命令行解析完成。只有现在 shell 才会查找以第一个单词命名的命令。到目前为止,命令是 echo 这一事实是无关紧要的。如果您说 cat \ \ \ \,将使用 4 个参数词调用 cat,每个参数词包含一个反斜杠。

通常当您 运行 echo 时,您将获得 shell 内置命令。 zsh 内置 echo 具有可配置的行为。我喜欢使用 setopt BSD_ECHO 到 select BSD 风格的回声行为,但从你的示例输出看来你处于默认模式,SysV 风格。

BSD 风格的 echo 不做任何反斜杠处理,它只会在收到它们时打印出来。

SysV echo 像在 C 字符串中一样处理反斜杠转义 - \t 变成制表符,\r 变成回车符 return,等等。另外 \c 被解释为"end the output without a newline".

所以如果你说 echo a\tb 那么 shell 解析会在给 echo 的参数词中产生一个反斜杠,echo 会解释 a\tb 并打印 ab 由制表符分隔。如果写成 echo 'a\tb' 会更具可读性,使用撇号在 shell-command-parsing 级别提供引用。同样 echo \\ 是命令行解析后的两个反斜杠,所以 echo 看到 \ 并输出一个反斜杠。如果您想按字面打印 a\tb 而不使用其他形式的引号,则必须说 echo a\\tb.

所以 shell 有一个简单的规则 - 命令行上的两个反斜杠在参数词中构成一个反斜杠。 echo 有一个简单的规则——参数词中的两个反斜杠在输出中形成一个反斜杠。

但是有一个问题......当回声做它的事情时,一个反斜杠后跟t意味着输出一个制表符,一个反斜杠后跟一个反斜杠意味着输出一个反斜杠......但是有很多不代表 任何东西 的组合。例如,后跟 T 的反斜杠不是有效的转义序列。在 C 中,这将是警告或错误。但是 echo 命令试图更宽容。

尝试 echo \Techo '\T',您会发现反斜杠后跟任何没有定义含义的反斜杠转义只会导致 echo 按原样输出两个字符.

最后一个例子:如果反斜杠后面没有跟任何东西怎么办?如果它是参数词中的最后一个字符怎么办?在那种情况下,echo 只输出反斜杠。

总之,参数词中的两个反斜杠导致输出中的一个反斜杠。但是参数单词中的一个反斜杠也会导致输出中出现一个反斜杠,如果它是单词中的最后一个字符,或者如果反斜杠与下一个字符一起没有形成有效的转义序列.

命令行echo \\因此变成单词列表

echo
\

输出单个反斜杠 "properly",并在所有级别应用引号。

命令行echo \变成单词列表

echo
\

输出单个反斜杠 "messily",因为 echo 在参数末尾发现了一个杂散的反斜杠,并且足够慷慨地为您输出它,即使它没有被转义。

其余的例子应该从这些原则中清楚。