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
并打印 a
和 b
由制表符分隔。如果写成 echo 'a\tb'
会更具可读性,使用撇号在 shell-command-parsing 级别提供引用。同样 echo \\
是命令行解析后的两个反斜杠,所以 echo 看到 \
并输出一个反斜杠。如果您想按字面打印 a\tb
而不使用其他形式的引号,则必须说 echo a\\tb
.
所以 shell 有一个简单的规则 - 命令行上的两个反斜杠在参数词中构成一个反斜杠。 echo 有一个简单的规则——参数词中的两个反斜杠在输出中形成一个反斜杠。
但是有一个问题......当回声做它的事情时,一个反斜杠后跟t
意味着输出一个制表符,一个反斜杠后跟一个反斜杠意味着输出一个反斜杠......但是有很多不代表 任何东西 的组合。例如,后跟 T
的反斜杠不是有效的转义序列。在 C 中,这将是警告或错误。但是 echo 命令试图更宽容。
尝试 echo \T
或 echo '\T'
,您会发现反斜杠后跟任何没有定义含义的反斜杠转义只会导致 echo 按原样输出两个字符.
最后一个例子:如果反斜杠后面没有跟任何东西怎么办?如果它是参数词中的最后一个字符怎么办?在那种情况下,echo 只输出反斜杠。
总之,参数词中的两个反斜杠导致输出中的一个反斜杠。但是参数单词中的一个反斜杠也会导致输出中出现一个反斜杠,如果它是单词中的最后一个字符,或者如果反斜杠与下一个字符一起没有形成有效的转义序列.
命令行echo \\
因此变成单词列表
echo
\
输出单个反斜杠 "properly",并在所有级别应用引号。
命令行echo \
变成单词列表
echo
\
输出单个反斜杠 "messily",因为 echo 在参数末尾发现了一个杂散的反斜杠,并且足够慷慨地为您输出它,即使它没有被转义。
其余的例子应该从这些原则中清楚。
当您尝试 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
并打印 a
和 b
由制表符分隔。如果写成 echo 'a\tb'
会更具可读性,使用撇号在 shell-command-parsing 级别提供引用。同样 echo \\
是命令行解析后的两个反斜杠,所以 echo 看到 \
并输出一个反斜杠。如果您想按字面打印 a\tb
而不使用其他形式的引号,则必须说 echo a\\tb
.
所以 shell 有一个简单的规则 - 命令行上的两个反斜杠在参数词中构成一个反斜杠。 echo 有一个简单的规则——参数词中的两个反斜杠在输出中形成一个反斜杠。
但是有一个问题......当回声做它的事情时,一个反斜杠后跟t
意味着输出一个制表符,一个反斜杠后跟一个反斜杠意味着输出一个反斜杠......但是有很多不代表 任何东西 的组合。例如,后跟 T
的反斜杠不是有效的转义序列。在 C 中,这将是警告或错误。但是 echo 命令试图更宽容。
尝试 echo \T
或 echo '\T'
,您会发现反斜杠后跟任何没有定义含义的反斜杠转义只会导致 echo 按原样输出两个字符.
最后一个例子:如果反斜杠后面没有跟任何东西怎么办?如果它是参数词中的最后一个字符怎么办?在那种情况下,echo 只输出反斜杠。
总之,参数词中的两个反斜杠导致输出中的一个反斜杠。但是参数单词中的一个反斜杠也会导致输出中出现一个反斜杠,如果它是单词中的最后一个字符,或者如果反斜杠与下一个字符一起没有形成有效的转义序列.
命令行echo \\
因此变成单词列表
echo
\
输出单个反斜杠 "properly",并在所有级别应用引号。
命令行echo \
变成单词列表
echo
\
输出单个反斜杠 "messily",因为 echo 在参数末尾发现了一个杂散的反斜杠,并且足够慷慨地为您输出它,即使它没有被转义。
其余的例子应该从这些原则中清楚。