4

我正在写一个 shell,我对POSIX shell 规范有点困惑。说我有命令:

echo "`echo "a\\
b"`"

shell应该输出

ab

或者

a\
b

?

换句话说,在命令替换中从文本中删除转义后是否再次删除了续行?POSIX 规范似乎指定不会再次发生行继续删除,但是,我测试的所有 shell(bash、dash 和 busybox 的 ash)再次运行行继续删除,导致测试脚本输出ab.

脚本解释:

命令替换中的脚本部分未转义,产生:

echo "a\
b"

现在,如果再次运行续行删除,它将删除反斜杠换行符对,在echo "ab"命令替换中生成命令,否则反斜杠换行符对仍将位于a和之间b

4

1 回答 1

2
  • 旧式 `...`命令替换将嵌入命令预先解释\为转义字符,然后才解析并执行它。

    在命令替换的反引号样式中,\应保留其字面含义,除非后跟:$、、`\

    • 换句话说:任何嵌入\$\`, 和\\序列都被视为转义序列,其第二个字符应按字面意思处理。

    • 因此,\\<newline>在您的命令中简化为\<newline>, 因为`...`将 解释\\转义的文字 \

    • 这种解释发生嵌入式命令被解析和执行之前。

    • 因此\<newline>,结果命令中的 in 被解释为行继续(在双引号字符串内),它有效地删除了换行符。

    • 因此,双引号字符串被有效地解析为 literal ab,这就是传递给内部echo调用的内容。

    • bash中,您可以通过设置调试选项来验证此处理:set -xv

  • 现代语法$(...) 通过提供真正独立的引用上下文来避免这种意外

    由于这些不一致的行为,对于嵌套命令替换或尝试嵌入复杂脚本的新应用程序,不建议使用反引号的各种命令替换

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

      echo "$(echo "a\\
      b")"
      
      # Output
      a\
      b         
      
    • 更喜欢的另一个原因$(...)是它在 、 和 中的工作方式相同bash,但dash对于,其行为不同(见下文)并非如此。kshzsh`...`ksh


主要类 POSIX 外壳的合规性 - bash, dash, ksh,zsh

  • ksh(使用 version 验证93u+)中,您的命令会中断,因为ksh 需要嵌入的"字符。内部`...`被转义为\"- 这是与标准的偏差。
    语法$(...)没有这个要求。

  • bash, dash, 并按照规范的要求zsh处理`...`基于 - 的命令(在 的情况下bash,无论它是否在 POSIX 兼容模式下运行)。

    • 请注意,这些 shell支持根据需要\"在内部以双引号形式`...`转义ksh
    • 可以说,支持这一点是对标准的偏离,因为在 ; 的上下文中,"不在形成转义序列的字符中。例如,应该导致,而不是。\`...`echo "`echo \"a b\"`""a b"a b

选读:跨壳测试

如果您发现自己需要经常比较类似 POSIX 的 shell 的行为,请考虑使用shall、my CLI 和 REPL 来调用带有多个类似 POSIX 的 shell 的 shell 脚本或命令。

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

例如,如果您将命令放在 script./tst中,您将调用shall如下:

shall ./tst

这会产生类似的东西:

<code>shall</code> 示例输出

请注意如何调用ksh失败,因为ksh需要"`...`命令替换中将其转义为\".
同样,使用$(...)会绕过这个问题。

shallnpm 注册表安装(Linux 和 macOS)

注意:即使你不使用 Node.js,npm它的包管理器也可以跨平台工作并且易于安装;尝试
curl -L https://git.io/n-install | bash

安装Node.js后,按如下方式安装:

[sudo] npm install shall -g

注意

  • 是否需要sudo取决于您如何安装 Node.js 以及您以后是否更改了权限;如果出现EACCES错误,请使用 重试sudo
  • -g确保全局安装,并且需要放入shall系统的$PATH.

手动安装(任何带有 的 Unix 平台bash

  • 将此bash脚本下载为shall.
  • 使其可执行chmod +x shall
  • 将其移动或符号链接到您的文件夹中$PATH,例如/usr/local/bin(macOS) 或/usr/bin(Linux)。
于 2017-05-08T14:17:12.370 回答