在此问题中,有人报告了一个使用here文档的问题,该文档在$(...)
命令替换中使用带引号的定界符单词,其中\
文档内部行末尾的反斜杠触发换行-换行,而在命令替换之外的相同here文档可以按预期工作。
这是一个简化的示例文档:
cat <<'EOT'
abc ` def
ghi \
jkl
EOT
这包括在行尾包含一个反引号和一个反斜杠。用引号引起来,因此主体内部不会发生扩展。在所有与伯恩相似的地方,我都能找到逐字逐句输出的内容。如果我将相同文档放在命令替换中,如下所示:
x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"
然后它们不再表现相同:
dash
,ash
,zsh
,ksh93
,BusyBox的ash
,mksh
,和SunOS 5.10 POSIXsh
都给予该文件的逐字内容,如前。- 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时,外壳程序应通过将下面的第一个适用规则应用于其输入中的下一个字符,将其输入分解为令牌。...
...
- 如果当前字符是<反斜杠>,单引号或双引号,并且未加引号,则它将影响到后续字符的引号,直到引号文本的末尾。对引用的规则中所描述的引用。在令牌识别期间,不得实际执行任何替换,并且结果令牌应完全包含输入中出现的字符(<newline>联接除外),且未修改,包括在末尾之间的任何嵌入式或封闭引号或替换运算符。引用文字的内容。
我对此的解释是,$(
直到终止为止的所有字符都)
逐字逐句地包含shell脚本;出现此处文档,因此将进行此处文档处理,而不是普通的标记化;然后,此处的文档带有引号的定界符,这意味着其内容是逐字处理的;并且转义字符永远不会进入。我可以看到一个论点,但是,这种情况根本没有解决,并且两种行为都是允许的。我也有可能在某个地方跳过了一些相关的文字。
- 这种情况在其他地方更清楚了吗?
- (在理论上)可移植脚本应该能够依靠什么?
- 标准要求这些外壳中的任何一个(Bash 3.2 / Bash 4.3 /其他任何对象)给予特定的处理吗?禁止的?可以吗
echo "$x"
,但是任何检查变量的方法都可以。我已经将该行编辑到底部。
$(...)
为输出内容...现在,当在示例中的子shell中运行该命令时,bash
它确实会输出预期的结果。只有将其转换为命令替换时,它才会折叠“ ghi”和“ jkl”。所以这是个错误imo