编辑以澄清我的问题:
POSIX 说:
如果 <newline> 跟在(不带引号的)<backslash> 后面,则 shell 会将其解释为行继续。在将输入拆分为标记之前,应删除 <backslash> 和 <newline>。
然而,dash
或其他实现,首先对输入进行标记。结果,\<newline>
不被识别而是# this is a comment \
被丢弃。这种行为符合 POSIX 标准吗? POSIX 再次指出在标记化之前应删除行延续。
下面的过程真的不符合 POSIX 标准吗?
- 读取整个输入:
"echo hello ... \<newline> ... bye"
- 搜索未引用的内容
\<newline>
并将其删除:"echo hello ... bye"
- 标记化:
"echo"(discard ' ')"hello"(discard ' ')(discard "# ... bye")
在 Ubuntu 上使用 dash-0.5.10.2-6 sh (dash) 我们得到以下内容
$ cat /var/tmp/test.sh
echo hello # this is a comment \
echo bye
$ sh /var/tmp/test.sh
hello
bye
这是因为 # 之后的所有内容都被视为注释,而 \ 之前的所有内容都被丢弃,因此 \<newline> 的续行不起作用。
然而,POSIX“转义字符(反斜杠)”部分状态
在将输入拆分为标记之前,应删除 <backslash> 和 <newline>。
echo hello # this is a comment \
echo bye
应该等于
echo hello # this is a comment echo bye
这是否意味着 sh 不符合 POSIX 标准?或者在这种情况下评论优先于行延续是否有某种理由?
答案1
shell 的输入被逐个字符地扫描以将其划分为标记,如第 1 节中所述。令牌识别。
[...] shell 应通过应用下面的第一个适用规则将其输入分解为标记到下一个字符在其输入中。
引用是作为令牌识别过程的一部分进行处理的,但考虑到问题中的示例,shell 将遇到#
引用的换行符之前。
当 shell 在扫描输入行期间到达不带引号的注释字符时,该行的其余部分(包括最后的反斜杠)将作为注释被丢弃:
如果当前字符是 a
#
,则它和所有后续字符(但不包括下一个字符)<newline>
将作为注释被丢弃。该<newline>
行末尾的 不被视为注释的一部分。
您引用的标准部分,引用部分,说当遇到前面有反斜杠的换行符时......
未加引号的A
<backslash>
应保留后续字符的字面值,但 a 除外<newline>
。如果 a<newline>
跟在 后面<backslash>
,shell 会将其解释为行继续。在<backslash>
将<newline>
输入拆分为标记之前应删除and 。 [...]
请注意,直到扫描仪实际遇到不带引号的反斜杠(由令牌识别过程处理)时,这才会生效:
如果当前字符是
<backslash>
、单引号或双引号并且未加引号,则它将影响后续字符的引用,直到被引用文本的末尾。引用规则如“引用”中所述。
正如本答案中已经提到的,扫描器将首先遇到注释字符,然后再看到反斜杠,这将触发令牌识别规则,该规则将行的其余部分(包括任何引用字符)作为注释处理。因此,行尾换行符的引用永远不会生效。
答案2
有趣的想法。您似乎认为反斜杠转义是在任何标记识别之前应用于整个输入的一个步骤,但事实并非如此。
输入仍然严格从左到右进行评估,这在不同的情况下是必要的,例如引用:
echo 'foo\
bar'
另一种情况是,由于上下文原因,所引用的规则不适用。在这种情况下,上下文是一个带引号的字符串,在您的情况下,它是根据的评论
如果当前字符是“#”,则它以及所有后续字符(不包括下一个字符)都将作为注释被丢弃。
引用句子的原因是你可以这样做
ec\
ho foo
所以你从左到右,逐个标记地进行。如果您仍在正常上下文中,则在标记拆分之前删除反斜杠+换行符,评估很简单echo
。
该行为不仅适用dash
于任何 POSIX shell。
答案3
感谢您的回答。
我知道我们知道\
在标记化时是否被引用,所以我们不能\<newline>
在标记化之前删除未引用的内容。
在将输入拆分为标记之前,应删除 <backslash> 和 <newline>。
这个声明只是说,与其他引用不同,\<newline>
它会立即被丢弃。我必须这样想。