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",否则反斜杠换行对将仍然在 ab 之间。

  • 旧式 `...` 命令替换使嵌入式命令服从先前\解释为转义符,只有然后解析并执行

    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.

    • 使用$(...),保留嵌入双引号字符串中的转义行继续(在bashdashkshzsh):

      echo "$(echo "a\
      b")"
      
      # Output
      a\
      b         
      
    • 另一个喜欢 $(...) 的原因是它在 bashdashkshzsh 中的作用相同, `...` 的行为与 ksh 不同(见下文)。


符合主要 POSIX-like shells - bashdashkshzsh

  • ksh(用版本93u+验证),你的命令breaks,因为ksh需要 嵌入" 个字符。在 `...` 内被转义为 \" - 这与标准有偏差。
    语法$(...)没有有这个要求。

  • bashdashzsh 按照规范要求处理基于 `...` 的命令(在 bash,不管是不是POSIX-兼容模式下的运行。

    • 请注意,这些 shells 支持 \"-转义为 `...` 内的双引号,因为 ksh 需要。
    • 可以说,支持这一点是偏离标准的,因为 " 而不是 在前面有 [=14= 时形成转义序列的字符中] 在 `...` 的上下文中;例如,echo "`echo \"a b\"`" 应该导致 "a b",而不是 a b

选读:交叉shell测试

如果您发现自己需要经常比较 POSIX-like shell 的行为,请考虑使用 shall、我的 CLI 和 REPL 来调用 shell具有多个 POSIX-like shells.

的脚本或命令

默认情况下,它的目标是 bashdashkshzsh(无论安装哪个)。

例如,如果您将命令放在脚本 ./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 scriptshall
  • 使用 chmod +x shall 使其可执行。
  • 将其移动或符号链接到您 $PATH 中的文件夹,例如 /usr/local/bin (macOS) 或 /usr/bin (Linux)。