POSIX在命令替换内对此处引用的文档有何要求?


20

此问题中,有人报告了一个使用here文档的问题,该文档$(...)命令替换中使用带引号的定界符单词,其中\文档内部行末尾的反斜杠触发换行-换行,而命令替换之外的相同here文档可以按预期工作。

这是一个简化的示例文档:

cat <<'EOT'
abc ` def
ghi \
jkl
EOT

这包括在行尾包含一个反引号和一个反斜杠。用引号引起来,因此主体内部不会发生扩展。在所有与伯恩相似的地方,我都能找到逐字逐句输出的内容。如果我将相同文档放在命令替换中,如下所示:

x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"

然后它们不再表现相同:

  • dashashzshksh93,BusyBox的ashmksh,和SunOS 5.10 POSIX sh都给予该文件的逐字内容,如前。
  • Bash 3.2针对不匹配的反引号给出了语法错误。使用匹配的反引号,它尝试将内容作为命令运行。
  • Bash 4.3将“ ghi”和“ jkl”折叠到一行上,但是没有错误。该--posix选项不影响此。Kusalananda 告诉我(谢谢!)的pdksh行为也是如此

在最初的问题中,我说这是Bash解析器中的错误。是吗?[更新: ]我可以从POSIX(全部来自Shell命令语言定义)中找到相关文本:

  • §2.6.3命令替换

    使用$(command)格式,在右括号后到匹配的右括号后的所有字符构成命令。可以将任何有效的Shell脚本用于命令,但仅由重定向组成的脚本会产生未指定的结果。

  • §2.7.4此处文档

    如果对单词的任何部分加引号,则应通过对单词执行引号删除来形成定界符,并且此处文档行不得扩展。

  • 第2.2.1节转义符(反斜杠)

    如果<newline>跟在<backslash>之后,则外壳程序应将其解释为行继续。在将输入拆分为令牌之前,应删除<backslash>和<newline>。

  • §2.3令牌识别

    io_here标记已被语法识别时(请参见Shell Grammar),紧接在下一个NEWLINE标记之后的一个或多个后续行构成一个或多个here-document的主体,并应根据Here-rule的规则进行解析-文件

    当不处理io_here时,外壳程序应通过将下面的第一个适用规则应用于其输入中的下一个字符,将其输入分解为令牌。...

    ...

    1. 如果当前字符是<反斜杠>,单引号或双引号,并且未加引号,则它将影响到后续字符的引号,直到引号文本的末尾。对引用的规则中所描述的引用。在令牌识别期间,不得实际执行任何替换,并且结果令牌应完全包含输入中出现的字符(<newline>联接除外),且未修改,包括在末尾之间的任何嵌入式或封闭引号或替换运算符。引用文字的内容。

我对此的解释是,$(直到终止为止的所有字符都)逐字逐句地包含shell脚本;出现此处文档,因此将进行此处文档处理,而不是普通的标记化;然后,此处的文档带有引号的定界符,这意味着其内容是逐字处理的;并且转义字符永远不会进入。我可以看到一个论点,但是,这种情况根本没有解决,并且两种行为都是允许的。我也有可能在某个地方跳过了一些相关的文字。


  • 这种情况在其他地方更清楚了吗?
  • (在理论上)可移植脚本应该能够依靠什么?
  • 标准要求这些外壳中的任何一个(Bash 3.2 / Bash 4.3 /其他任何对象)给予特定的处理吗?禁止的?可以吗

您能告诉我们在第二种情况下如何产生输出吗?
朱莉·佩勒

@JuliePelletier echo "$x",但是任何检查变量的方法都可以。我已经将该行编辑到底部。
迈克尔·荷马”,

2
看起来很容易解决。这个补丁似乎至少可以正常工作:ignore_quoted_newline_in_quoted_heredoc.patch
geirha '17

1
我认为您正在正确地理解这一点,而且imo的标准也很明确,因为“外壳程序应通过在子shell环境中执行命令并用的标准输出替换命令替换来扩展命令替换。 [...]“命令,因此它将在子shell中运行该命令,并替换$(...)为输出内容...现在,当在示例中的子shell中运行该命令时,bash它确实会输出预期的结果。只有将其转换为命令替换时,它才会折叠“ ghi”和“ jkl”。所以这是个错误imo
don_crissti

2
@geirha我报告了一个Bash错误 ; 我不会为pdksh烦恼,因为它似乎甚至没有当前维护的影子。
Michael Homer

Answers:


5

在Bash的邮件列表中询问了此消息,维护人员确认这是一个错误

他们还提到POSIX中的文本“不一定是模棱两可的,但确实需要仔细阅读。”,所以我要求对此进行澄清。他们的回答包括对问题的描述和对标准的解释,如下:

命令替换是一条红色的鲱鱼。仅在指出错误所在的地方才有意义。

引用本文的定界符,因此行不会扩展。在这种情况下,外壳程序会从输入中读取行,就像它们被引用一样。如果在引用的上下文中出现反斜杠,则它不会充当转义符(请参见下文),并且不会对反斜杠-换行符进行特殊处理。实际上,如果在定界符的任何部分都加上了引号,则将此处文档行读为单引号。

Posix 2.2.1中的文本写得很笨拙,但是这意味着仅在不加反引号的情况下才对反斜杠进行特殊处理。您可以使用反斜杠加引号,并仅用单引号或其他反斜杠禁止所有扩展。

仔细阅读的部分是“未扩展”文本,表示单引号。该标准在2.2中说,这里的文档是“另一种报价形式”,但是唯一根本不扩展单词的唯一报价形式是单引号。因此,这是一种几乎完全像单引号一样但不是单引号的报价形式。


@Scott(1)我相信这可以回答所有问题,没有多余的东西。我的评论从答案开始,是关于一位主持人删除的,他误解了情况。(2)我没有足够的声誉。(3)通过删除我的答案,我会很欣赏类似的行为,但是将来我一定会记住这一点。感谢您的想法。
凯文

我的意思是,您的第一段大部分内容是与Michael Mrozek的对话,而不是问题的答案。我意识到您没有足够的声誉来评论任何帖子,但我相信您有足够的元信息和聊天记录。
斯科特,

1
@Scott我理解并赞赏您正在尝试精简答案,但我之前曾发布过该精简答案(仅是引号和指向它的链接),并且由主持人删除(没有任何讨论!),我在已删除的帖子中看不到任何链接,以聊天和质疑该决定。我希望通过回答他的毫无根据的批评,它可以在删除后继续存在,并被问询者接受,然后我将修改答案以删除序言。
凯文
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.