Zsh:为什么 \n 在单引号内解释?

Zsh: Why is \n interpreted within single quotes?

根据我的理解,\n 应该被解释为

中的新行
"a\nb"
$'a\nb'

但不在

'a\nb'

但是,我看到了以下内容:

(我的 zsh 会话记录如下)

-0-1- ~  > zsh --version
zsh 5.1.1 (x86_64-unknown-cygwin)
-0-1- ~  > echo 'a\nb'
a
b
-0-1- ~  >

换行是字符,不是变量,所以没有解释(或扩展)。如果要打印a\,需要转义两次表示不想转义一个变量和不想打印a的特殊字符。

所以你必须使用echo a\\nbecho 'a\nb'

您对 "a\nb" 的看法是错误的,它 而不是 \n 替换为换行符(好吧,无论如何都不是可移植的)。所以没有标准的引用机制允许字符串中的换行符而不使用文字换行符,如

x="a
b"

这就是发明 $'a\nb' 符号的原因。它不是 2016 年的 POSIX,但 POSIX 的人正在考虑将它添加到 shell specification。一如既往,不要屏住呼吸:-)

zsh 本身不解释 'a\nb' 中的 \n,但内置 echo 可以。 'a\nb'"a\nb" 是等效的,不包含换行符但包含(文字)字符序列 \n$'a\nb' 另一方面,实际上确实包含换行符。

这里有两件事在起作用:

1。引用:

引号用于告诉 shell 你想要一个字符本身,而不是它在 shell 语法中可能具有的任何特殊含义。 zsh有四种引号,其中一些可能会保留或增加一些字符或字符序列的特殊含义:

  1. \:通过前缀 \ 来引用单个字符。因为 zsh\n 只意味着 "I want the character n"。由于 n 没有特殊意义,所以和只写 n 是一样的。这随着 * 之类的字符而改变:不加引号 * 用作 globbing 的通配符,编写 \* 可以防止这种情况发生(例如:比较 echo /* 和 [=32 的输出=]).如果你想按字面意思传递字符 \,你必须引用它,例如用另一个 \: \.
  2. '...':就像 \ 一样,任何带有 '...' 的字符都是按字面意思理解的,这包括其他引用方法。 'foo* bar?' 本质上等同于 foo\*\ bar\?(或者 \f\o\o\*\ \b\a\r\?,如果有人想学究气的话)。只有 ' 本身不能出现在用 '...' 引用的字符串中,因为它会标记 qoute 的结尾。 (这可以通过设置RC_QUOTES来改变。如果设置一对单引号作为单引号:'foo''bar'foo\'bar
  3. "...":允许参数和命令替换。这意味着 $ 之后的单词被用作参数名称(例如 "path: $PATH"),并且包裹在 $(...)`...` 中的字符串用于命令替换(例如 echo "Date: $(date)" ).否则它的行为类似于 '...' 除了它允许 `$\"\.
  4. $'...'$'...' 中的字符串被视为 print 内置函数的字符串参数。只有在这里一些字母字符确实有特殊含义:\n换行,\b退格,\a响铃等。\'可以分别用\\'引用。结果字符串被认为是完全引用的。在 $'...'.
  5. 中没有 参数或命令替换

2。 zsh-builtin echo:

除了 echo 二进制文件或 bash-builtin echo 之外,zsh-builtin 默认识别(一些)转义序列,如 \n\t.虽然此行为通常需要使用 /bin/echo 或 bash-builtin 显式启用(通常通过传递 -e 标志:echo -e "foo\nbar"),但对于它需要的 zsh-builtin被明确禁用。通过传递 -E 标志(echo -E 'foo\nbar')或设置 BSD_ECHO 选项(setopt bsdecho),在这种情况下,-e 标志可用于重新像其他类型的 echo.

一样启用该功能

结论

这意味着 'a\nb'"a\nb"(以及 a\nb 就此而言)都作为 a\nb(字面意思)传递,但是 zsh-builtin echo 然后解释 \n,导致带有换行符的输出。另一方面,$'a\nb' 在传递给 echo.

之前已经包含了一个文字换行符

运行

for quoted_string in a\nb 'a\nb' "a\nb" $'a\nb'; do
    echo '>' $quoted_string '<'
    /bin/echo -e '>' $quoted_string '<'
    echo -E '>' $quoted_string '<'
    /bin/echo '>' $quoted_string '<'
    echo
done

应该得到以下输出:

> a
b <
> a
b <
> a\nb <
> a\nb <

> a
b <
> a
b <
> a\nb <
> a\nb <

> a
b <
> a
b <
> a\nb <
> a\nb <

> a
b <
> a
b <
> a
b <
> a
b <

如您所见,前三种引用之间没有区别,而第四种总是打印换行符。


顺便说一句:perl(至少版本 5;我不知道 Perl 6)的行为方式与您描述的预期行为方式相同。在 perl 中,'...' 的行为与在 zsh 中的行为相同。另一方面,perl 中的 "..." 表现得像 zsh"..."$'...' 的组合:变量被它们的值和字符序列替换,如 \n\t 被特殊对待。