管道传输heredoc的多行语法; 这是可移植的吗?

157

我熟悉这种语法:

cmd1 << EOF | cmd2
text
EOF

但我刚刚发现bash允许我这样写:

cmd1 << EOF |
text
EOF
cmd2

(heredoc 用作输入到 cmd1 中,而 cmd1 的输出被管道传输到 cmd2)。这似乎是一种非常奇怪的语法。它是否可移植?


我来这里是为了找到一个好的方法将这个命令拆分成多行:big-long-command1 with lots of args << EOF | big-long-command2 with lots of args。这种“奇怪的语法”似乎是最好的方式。 - PaulC
一个方便的使用案例是当你想要将以空格分隔的表格转换为以制表符分隔的表格,这样你就可以将其粘贴到Google电子表格中。你不需要创建临时文件。 - Sridhar Sarnobat
第一个在我的z-shell中不起作用。我不喜欢第二个,因为它将|与命令隔离开来,失去了shell管道的惯用语感。 - Sridhar Sarnobat
4个回答

121

是的,POSIX标准允许这样做。根据2008版本的规定:

Here-document应被视为一个单词,它从下一个<newline>开始,一直持续到只包含定界符和<newline> 的行,其中没有任何<blank>字符。然后,如果有下一个here-document,则开始下一个。

并包括同一行中多个 "here-documents" 的示例:

cat <<eof1; cat <<eof2
Hi,
eof1
Helene.
eof2

所以重定向或管道没有问题。你的示例类似于以下内容:

cat file |
cmd

而且 shell 语法(在链接页面的下方)包括以下定义:

pipe_sequence    :                             command
                 | pipe_sequence '|' linebreak command

newline_list     :              NEWLINE
                 | newline_list NEWLINE
                 ;
linebreak        : newline_list
                 | /* empty */
所以,管道符号可以跟随行尾并仍被视为管道线的一部分。

28

是的,它在POSIX shell语法中。您还可以为同一命令拥有多个here-doc(其他示例使用两个cat调用,但这也可以工作):

cat <<EOF1 <<EOF2
first here-doc
EOF1
second here-doc
EOF2
这是一个人为的例子(使用两个 here-docs 作为标准输入),但如果你考虑为不同的文件描述符提供输入,它立即就有意义了。 还有一种可能性是drop the cat entirely。为什么不直接将 here-document 提供给 cmd:
cmd << EOF
input
here
EOF

1
以下代码无法正常工作:cat < - user1424739
@user1424739,它在当前的zsh和bash中可以工作。ash和ksh93似乎只输出第二个here doc。 - Jens
为什么要点踩?如果有不准确的地方,请给我机会进行修正。 - Jens
当使用 sudo tee /etc/securefile.conf <<EOF 时,这非常方便。 - dragon788
3
它在哪个Bash版本上工作?使用Bash 4.4.19(在Ubuntu 18.04.02上)和Bash 5.0(Docker映像),我只得到了第二个Here-doc。或者说可能有一个特定的选项吗? - huelbois
@huelbois 我也是在bash 5.1.8上遇到了同样的问题。 - Cigarette Smoking Man

19

嗯,根据在 POSIX 模式下在 bash 中的测试,我想是的:

$ bash --posix
$ cat <<EOF |
> ahoj
> nazdar
> EOF
> sed 's/a/b/'
bhoj
nbzdar

只有另一个小提示:不要在结束的 EOF 后面加任何空格。否则命令提示符会表现奇怪,你会想弄清楚到底出了什么问题。 - Sridhar Sarnobat
2
在 POSIX 模式下运行 bash 会关闭一些扩展,但绝不是全部。因此,虽然这个答案在 POSIX 允许的范围内是正确的,但它的推理并不是非常有效的支持。 - Charles Duffy

9

嗨,以此为例进行检查

#!/bin/sh
( base32 -d | base64 -d )<<ENDOFTEXT
KNDWW42DNNSHS5ZXPJCG4MSVM5MVQVT2JFCTK3DELBFDCY2IIJYGE2JUJNHWS22LINVHQMCMNVFD
CWJQIIZVUV2JOVNEOVJLINTW6PIK
ENDOFTEXT

致敬


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接