为什么进程替换并不总是适用于 bash 中的 while 循环?

why process substitution does not always work with while loop in bash?

process substitution 可以很好地处理文件名,例如两者

$ cat <FILENAME

$ while read i; do echo $i; done <FILENAME

工作。

但是如果我们使用 echo 命令(或任何其他生成输出到标准输出的命令)而不是 FILENAME,cat 继续工作

$ cat <(echo XXX)
XXX

while循环

$ while read i; do echo $i; done <(echo XXX) 
bash: syntax error near unexpected token `<(echo XXX)'

产生错误。

知道为什么吗?

我为我的愚蠢道歉,刚刚找到了可行的解决方案

$ while read i; do echo $i; done < <(echo XXX)
XXX

如果有人能用双少符号解释这种奇怪语法背后的想法,我将非常感激,< <

注:<b><</b><em>文件名</em>进程替换。这是一个redirection。进程替换的格式为 <b><(</b><em>command</em><b>)</b>

进程替换用 进程名称 替换 <(...)。尽管使用了 < 符号,但它不是重定向。

所以当你说 cat <(echo foo) 时,bash 创建一个子进程到 运行 echo 命令,并替换一个可以读取的伪文件的名称获取该命令的输出。替换的结果将是这样的:

cat /dev/fd/63

注意没有重定向。 (您可以通过键入 echo <(echo foo) 来查看实际效果。)

像许多实用程序一样,cat 可以使用或不使用命令行参数来调用;如果未指定文件,则从 stdin 读取。所以 cat file.txtcat < file.txt 非常相似。

但是while命令不接受额外的参数。所以

while read -r line; do echo "$line"; done < file.txt 

有效,但是

while read -r line; do echo "$line"; done file.txt

是语法错误。

进程替换不会改变这一点。所以

while read -r line; do echo "$line"; done /dev/fd/63

是语法错误,因此

也是
while read -r line; do echo "$line"; done <(echo foo)

要从进程替换指定重定向,您需要一个重定向:

while read -r line; do echo "$line"; done < <(echo foo)

请注意,两个 < 符号之间必须有一个 space,以避免与 "here-doc" 语法 <<word 混淆。

进程替换操作生成一个文件名,通常在 /dev/fd 中,尽管这不是强制性的。需要用这些术语来考虑;它可以用在可以使用文件名的地方。

你可以看看它的作用,例如:

echo <(cat /etc/passwd)

你的例子说明了这一点。 cat 命令接受文件名参数。 done 关键字不接受文件名参数,但接受 I/O 重定向。

您可以确定该符号令人困惑;在某些方面,它是。但抱怨毫无意义。 Bash 就是这样做的。您可以使用不同的表示法自由创建自己的 shell,但出于向后兼容性的原因,Bash 将继续支持当前的表示法。