POSIX shell: 在反引号命令替换中转义行继续
POSIX shell: escaping line-continuations in backquote command-substitutions
我正在写一个shell,我对POSIX shell specification有点困惑。假设我有命令:
echo "`echo "a\
b"`"
shell 是否应该输出
ab
或
a\
b
?
换句话说,在删除命令替换中的文本转义后,是否再次删除了续行? POSIX 规范似乎指定行继续删除不会再次发生,但是,我测试的所有 shells(bash、破折号和 busybox 的灰烬)运行再次删除行继续,导致测试脚本输出 ab
.
脚本解释:
命令替换中的脚本部分未转义,生成:
echo "a\
b"
现在,如果继续删除再次为 运行,它将删除反斜杠换行对,在命令替换中生成命令 echo "ab"
,否则反斜杠换行对将仍然在 a
和 b
之间。
旧式 `...`
命令替换使嵌入式命令服从先前 将\
解释为转义符,只有然后解析并执行
- POSIX shell spec 中的这段话是关键(为便于阅读而编辑):
Within the backquoted style of command substitution, \
shall retain its literal meaning, except when followed by: $
, `
, or \
.
换句话说:任何嵌入的$
、\`
和\
序列都被视为转义序列其第二个字符应按字面意思处理。
因此,您命令中的 \<newline>
减少为 \<newline>
,因为 `...`
将 \
解释为 转义, 文字 \
这种解释发生在之前 嵌入命令被解析和执行。
结果命令中的 \<newline>
因此被解释为 行继续 (在双引号字符串内),这实际上 删除换行符。
因此,双引号字符串被有效地解析为文字 ab
,这就是传递给内部 echo
调用的内容。
在bash
中,你可以通过设置调试选项来验证这个处理:set -xv
现代语法$(...)
避免了这种意外通过提供真正独立的引用上下文。
Because of these inconsistent behaviors, the backquoted variety of command substitution is not recommended for new applications that nest command substitutions or attempt to embed complex scripts.
使用$(...)
,保留嵌入双引号字符串中的转义行继续(在bash
、dash
、ksh
和zsh
):
echo "$(echo "a\
b")"
# Output
a\
b
另一个喜欢 $(...)
的原因是它在 bash
、dash
、ksh
和 zsh
中的作用相同, `...`
的行为与 ksh
不同(见下文)。
符合主要 POSIX-like shells - bash
、dash
、ksh
、zsh
在ksh
(用版本93u+
验证),你的命令breaks,因为ksh
需要 嵌入"
个字符。在 `...`
内被转义为 \"
- 这与标准有偏差。
语法$(...)
没有有这个要求。
bash
、dash
和 zsh
按照规范要求处理基于 `...`
的命令(在 bash
,不管是不是POSIX-兼容模式下的运行。
- 请注意,这些 shells 也 支持
\"
-转义为 `...`
内的双引号,因为 ksh
需要。
- 可以说,支持这一点是偏离标准的,因为
"
是 而不是 在前面有 [=14= 时形成转义序列的字符中] 在 `...`
的上下文中;例如,echo "`echo \"a b\"`"
应该导致 "a b"
,而不是 a b
。
选读:交叉shell测试
如果您发现自己需要经常比较 POSIX-like shell 的行为,请考虑使用 shall
、我的 CLI 和 REPL 来调用 shell具有多个 POSIX-like shells.
的脚本或命令
默认情况下,它的目标是 bash
、dash
、ksh
和 zsh
(无论安装哪个)。
例如,如果您将命令放在脚本 ./tst
中,您将按如下方式调用 shall
:
shall ./tst
产生类似的东西:
请注意 ksh
的调用是如何失败的,因为 ksh
需要 `...`
命令替换中的 "
转义为 \"
.
同样,使用 $(...)
可以绕过这个问题。
从 npm registry(Linux 和 macOS)安装 shall
注意:即使您不使用Node.js、npm
,它的包管理器也可以跨平台工作并且易于安装;尝试
curl -L https://git.io/n-install | bash
安装了Node.js,安装如下:
[sudo] npm install shall -g
注:
- 是否需要
sudo
取决于您的安装方式Node.js以及您是否changed permissions later;如果出现 EACCES
错误,请使用 sudo
. 重试
-
-g
确保 global installation 并且需要将 shall
放入系统的 $PATH
.
手动安装(任何具有 bash
的 Unix 平台)
- 下载 this
bash
script 为 shall
。
- 使用
chmod +x shall
使其可执行。
- 将其移动或符号链接到您
$PATH
中的文件夹,例如 /usr/local/bin
(macOS) 或 /usr/bin
(Linux)。
我正在写一个shell,我对POSIX shell specification有点困惑。假设我有命令:
echo "`echo "a\
b"`"
shell 是否应该输出
ab
或
a\
b
?
换句话说,在删除命令替换中的文本转义后,是否再次删除了续行? POSIX 规范似乎指定行继续删除不会再次发生,但是,我测试的所有 shells(bash、破折号和 busybox 的灰烬)运行再次删除行继续,导致测试脚本输出 ab
.
脚本解释:
命令替换中的脚本部分未转义,生成:
echo "a\
b"
现在,如果继续删除再次为 运行,它将删除反斜杠换行对,在命令替换中生成命令 echo "ab"
,否则反斜杠换行对将仍然在 a
和 b
之间。
旧式
`...`
命令替换使嵌入式命令服从先前 将\
解释为转义符,只有然后解析并执行- POSIX shell spec 中的这段话是关键(为便于阅读而编辑):
Within the backquoted style of command substitution,
\
shall retain its literal meaning, except when followed by:$
,`
, or\
.换句话说:任何嵌入的
$
、\`
和\
序列都被视为转义序列其第二个字符应按字面意思处理。因此,您命令中的
\<newline>
减少为\<newline>
,因为`...`
将\
解释为 转义, 文字\
这种解释发生在之前 嵌入命令被解析和执行。
结果命令中的
\<newline>
因此被解释为 行继续 (在双引号字符串内),这实际上 删除换行符。因此,双引号字符串被有效地解析为文字
ab
,这就是传递给内部echo
调用的内容。在
bash
中,你可以通过设置调试选项来验证这个处理:set -xv
现代语法
$(...)
避免了这种意外通过提供真正独立的引用上下文。Because of these inconsistent behaviors, the backquoted variety of command substitution is not recommended for new applications that nest command substitutions or attempt to embed complex scripts.
使用
$(...)
,保留嵌入双引号字符串中的转义行继续(在bash
、dash
、ksh
和zsh
):echo "$(echo "a\ b")" # Output a\ b
另一个喜欢
$(...)
的原因是它在bash
、dash
、ksh
和zsh
中的作用相同,`...`
的行为与ksh
不同(见下文)。
符合主要 POSIX-like shells - bash
、dash
、ksh
、zsh
在
ksh
(用版本93u+
验证),你的命令breaks,因为ksh
需要 嵌入"
个字符。在`...`
内被转义为\"
- 这与标准有偏差。
语法$(...)
没有有这个要求。bash
、dash
和zsh
按照规范要求处理基于`...`
的命令(在bash
,不管是不是POSIX-兼容模式下的运行。- 请注意,这些 shells 也 支持
\"
-转义为`...`
内的双引号,因为ksh
需要。 - 可以说,支持这一点是偏离标准的,因为
"
是 而不是 在前面有 [=14= 时形成转义序列的字符中] 在`...`
的上下文中;例如,echo "`echo \"a b\"`"
应该导致"a b"
,而不是a b
。
- 请注意,这些 shells 也 支持
选读:交叉shell测试
如果您发现自己需要经常比较 POSIX-like shell 的行为,请考虑使用 shall
、我的 CLI 和 REPL 来调用 shell具有多个 POSIX-like shells.
默认情况下,它的目标是 bash
、dash
、ksh
和 zsh
(无论安装哪个)。
例如,如果您将命令放在脚本 ./tst
中,您将按如下方式调用 shall
:
shall ./tst
产生类似的东西:
请注意 ksh
的调用是如何失败的,因为 ksh
需要 `...`
命令替换中的 "
转义为 \"
.
同样,使用 $(...)
可以绕过这个问题。
从 npm registry(Linux 和 macOS)安装 shall
注意:即使您不使用Node.js、npm
,它的包管理器也可以跨平台工作并且易于安装;尝试
curl -L https://git.io/n-install | bash
安装了Node.js,安装如下:
[sudo] npm install shall -g
注:
- 是否需要
sudo
取决于您的安装方式Node.js以及您是否changed permissions later;如果出现EACCES
错误,请使用sudo
. 重试
-
-g
确保 global installation 并且需要将shall
放入系统的$PATH
.
手动安装(任何具有 bash
的 Unix 平台)
- 下载 this
bash
script 为shall
。 - 使用
chmod +x shall
使其可执行。 - 将其移动或符号链接到您
$PATH
中的文件夹,例如/usr/local/bin
(macOS) 或/usr/bin
(Linux)。