POSIX shell: escaping line-continuations in backquote command-substitutions
我正在写一个shell,我对POSIX shell规范有些困惑。说我有命令:
1 2 | echo"`echo"a\\\\ b"`" |
shell输出是否应该
1 | ab |
或
1 2 | a\\ b |
?
换句话说,在命令替换中从文本中删除转义符后,是否再次删除了行继续符? POSIX规范似乎指定不再进行行连续删除,但是,我测试的所有shell(bash,破折号和busybox的灰烬)都再次运行了行连续删除,导致测试脚本输出
脚本说明:
命令替换内的脚本部分未转义,产生:
1 2 | echo"a\\ b" |
现在,如果再次运行换行删除,它将删除反斜杠-换行符对,并在命令替换内生成命令
-
旧式
`...` 命令替换使嵌入式命令先将\\ 解释为转义字符,然后才解析并执行它。- POSIX Shell规范的这一段很关键(为便于阅读而进行了编辑):
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
-
现代语法
$(...) 通过提供真正独立的引用上下文来避免此类意外。- 引用规范的"shell和实用程序规定"部分(添加了重点):
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 中):1
2
3
4
5
6echo"$(echo"a\\\\
b")"
# Output
a\\
b -
首选
$(...) 的另一个原因是,它在bash ,dash ,ksh 和zsh 中的工作原理相同,而对于`...` 却不成立,而`...` 的行为在ksh 中有所不同(参见下文)。
在类似POSIX的主要shell中的合规性-
-
在
ksh (已通过版本93u+ 验证)中,您的命令会中断,因为ksh 需要嵌入的" 字符。`...` 中的内部要转义为" -这是与标准的偏差。
语法$(...) 没有此要求。 -
bash ,dash 和zsh 根据规范要求处理基于`...` 的命令(对于bash ,无论它是否以POSIX兼容模式运行)。-
请注意,根据
ksh 的要求,这些shell程序还支持" -在`...` 中作为双引号转义。 -
可以说,支持此做法是对标准的偏离,因为在
`...` 上下文中,当" 不在以\\ 开头的转义序列中。例如,echo"`echo "a b"`" 应该生成"a b" ,而不是a b 。
-
请注意,根据
可选阅读:跨shell测试
如果您发现自己需要经常比较类似POSIX的shell的行为,请考虑使用
默认情况下,它的目标是
例如,如果将命令放在脚本
1 | shall ./tst |
其结果类似于:
请注意如何用
同样,使用
从npm注册表中安装
注意:即使不使用Node.js,它的包管理器
安装了Node.js之后,按以下步骤安装:
1 | [sudo] npm install shall -g |
注意:
-
是否需要
sudo 取决于安装Node.js的方式以及以后是否更改了权限。如果出现EACCES 错误,请使用sudo 重试。 -
-g 确保全局安装,需要将shall 放入系统的$PATH 中。
手动安装(任何带有
-
将此
bash 脚本下载为shall 。 -
使用
chmod +x shall 使它可执行。 -
将其移动或符号链接到
$PATH 中的文件夹,例如/usr/local/bin (macOS)或/usr/bin (Linux)。