Mic*_*mer 21 shell-script posix command-substitution here-document
在如何将 HEREDOC 文本带入 shell 脚本变量中?有人报告了一个问题,使用here 文档在$(...)命令替换内使用带引号的分隔符,其中\文档内行尾的反斜杠触发换行符连接行继续,而命令替换外的相同 here 文档按预期工作。
这是一个简化的示例文档:
cat <<'EOT'
abc ` def
ghi \
jkl
EOT
Run Code Online (Sandbox Code Playgroud)
这包括一行末尾的一个反引号和一个反斜杠。分隔符被引用,因此正文内部不会发生扩展。在所有 Bourne-alikes 中,我可以找到逐字输出内容。如果我将相同的文档放在命令替换中,如下所示:
x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"
Run Code Online (Sandbox Code Playgroud)
那么它们的行为不再相同:
dash、ash、zsh、ksh93、 BusyBox ash、mksh和 SunOS 5.10 POSIXsh都像以前一样给出了文档的逐字内容。--posix选项不影响此。Kusalananda告诉我(谢谢!)它的pdksh行为方式相同。在最初的问题中,我说这是 Bash 解析器中的一个错误。是吗?[更新:是] 我能找到的来自 POSIX 的相关文本(全部来自 Shell 命令语言定义)是:
在 $(command) 形式中,从左括号到匹配右括号的所有字符构成命令。任何有效的 shell 脚本都可以用于command,除了仅由重定向组成的脚本会产生未指定的结果。
如果word 的任何部分被引用,则应通过对word进行引用删除来形成分隔符,并且不得扩展 here-document 行。
如果 <newline> 跟在 <backslash> 之后,shell 会将其解释为行继续。<backslash> 和 <newline> 应在将输入拆分为标记之前删除。
当io_here标记已被语法识别(请参阅Shell Grammar),紧随下一个NEWLINE标记之后的一行或多行组成一个或多个 here-documents 的正文,并应根据Here-的规则进行解析文件。
当它不处理io_here 时,shell 应通过将下面的第一个适用规则应用于其输入中的下一个字符来将其输入分解为标记。...
...
- 如果当前字符是<反斜杠>、单引号或双引号并且没有被引用,它将影响到引用文本末尾的后续字符的引用。引用规则如Quoting 中所述。在标记识别期间,不应实际执行任何替换,结果标记应准确包含出现在输入中的字符(<newline> 连接除外),未修改,包括任何嵌入或封闭引号或替换运算符,位于输入和结尾之间引用的文本。
我对此的解释是,$(直到终止之前的所有字符都)包含逐字逐字的 shell 脚本;出现了 here 文档,因此发生了 here-document 处理而不是普通的标记化;这里的文档然后有一个带引号的分隔符,这意味着它的内容是逐字处理的;并且转义字符永远不会出现。然而,我可以看到一个论点,即这种情况根本没有得到解决,这两种行为都是允许的。我也可能在某处跳过了一些相关文本。
这是在 Bash 的邮件列表上询问的,维护者确认这是一个错误
他们还提到 POSIX 中的文本“不一定含糊不清,但确实需要仔细阅读。”,所以我要求对此进行澄清。他们的回答包括对问题的描述和对标准的解释如下:
命令替换是一个红鲱鱼;它的相关性仅在于它指出了错误所在。
引用 here-document 的分隔符,因此不会扩展行。在这种情况下,shell 从输入中读取行,就像它们被引用一样。如果反斜杠出现在引用它的上下文中,它不会充当转义字符(见下文),并且不会发生反斜杠换行符的特殊处理。事实上,如果分隔符的任何部分被引用,则 here-document 行将被视为单引号。
Posix 2.2.1 中的文本写得很笨拙,但这意味着反斜杠只有在没有引用时才会被特殊处理。您可以引用反斜杠并仅使用单引号或另一个反斜杠禁止所有扩展。
仔细阅读部分是暗示单引号的“未扩展”文本。标准在 2.2 中说,这里的文档是“另一种引用形式”,但唯一完全不扩展单词的引用形式是单引号。所以它是一种与单引号完全一样的引用形式,但不是单引号。
| 归档时间: |
|
| 查看次数: |
1938 次 |
| 最近记录: |