我想知道如何理解以下内容:


将命令的stdout输入另一个命令的stdin是一项强大的技术。但是,如果您需要通过管道传送多个
命令的标准输出怎么办?这就是进程替代的源头。换句话说,进程替代可以执行管道可以做的任何事情吗?

进程替代可以做什么,但是管道不能做什么?

#1 楼

解决它们之间差异的一个好方法是在命令行上做一些试验。尽管使用<字符在视觉上具有相似性,但它与重定向或管道的功能却大不相同。

让我们使用date命令进行测试。 br />
这是一个毫无意义的示例,但它表明cat接受了STDIN上date的输出并将其吐回去。通过流程替换可以实现相同的结果:

$ date | cat
Thu Jul 21 12:39:18 EEST 2011


但是,幕后发生的事情却有所不同。 cat实际上没有传递给STDIN流,而是传递给它需要打开并读取的文件名。您可以使用echo而不是cat来查看此步骤。

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011


当cat收到文件名时,它将为我们读取文件内容。另一方面,echo只是向我们显示了已通过的文件名。如果您添加更多替换,则这种区别变得更加明显:

STDIN):

$ echo <(date)
/proc/self/fd/11


看起来几乎一样,但是这次猫是通过STDIN流而不是文件名传递的。您可以通过使用echo尝试查看它:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13


由于echo无法读取STDIN且未传递任何参数,因此我们一无所获。

管道和输入重定向将内容推送到STDIN流上。进程替换运行命令,将其输出保存到特殊的临时文件中,然后传递该文件名代替命令。无论您使用什么命令,都将其视为文件名。请注意,创建的文件不是常规文件,而是一个命名管道,一旦不再需要该管道便会自动删除。

评论


如果我正确理解,tldp.org / LDP / abs / html / process-sub.html#FTN.AEN18244表示进程替换会创建临时文件,而不是命名管道。据我所知,不会创建临时文件。写入管道永远不会涉及写入磁盘:stackoverflow.com/a/6977599/788700

– Adob​​e
17 Dec 17 '13:13

我知道这个答案是合法的,因为它使用了grok这个词:D

– aqn
18年1月1日于13:46

@Adobe您可以确认替换产生的临时文件进程是否为带有[[-p <(date)]] && echo true的命名管道。当我使用bash 4.4或3.2运行它时,会产生true。

– De Novo
19年1月28日在19:56

#2 楼

这是您可以用过程替换完成的三件事,否则它们是不可能的。
多个过程输入
diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

用管道根本无法做到这一点。 />假设您有以下内容:
curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

您想直接运行它。以下失败了。 Bash已经在使用STDIN来读取脚本,因此其他输入是不可能的。
curl -o - http://example.com/script.sh | bash 

,但是这种方式很完美。
bash <(curl -o - http://example.com/script.sh)

出站进程替换
还要注意流程替代也以另一种方式起作用。因此,您可以这样做:出现“权限被拒绝”错误,然后将THOSE结果发送到文件。
请注意,第一个命令和stdout重定向位于括号(子外壳)中,因此只有THAT命令的结果发送到/dev/null,而不会不会弄乱其余部分。

评论


值得注意的是,在diff示例中,您可能要关心cd可能失败的情况:diff <(cd / foo / bar / && ls)<(cd / foo / baz && ls)。

–phk
16 Mar 6 '16 at 12:03

“在管道传输stderr时”:不是要不是管道传输而是要通过fifo文件吗?

–Gauthier
16年8月25日在7:59

@Gauthier没有;该命令不是用fifo而是用对文件描述符的引用代替。因此,“ echo <(echo)”应产生类似“ / dev / fd / 63”的信息,这是一种特殊字符设备,可从FD数字63读取或写入。

– tylerl
16年8月26日在2:40

#3 楼

我应该假设您在谈论bash或其他高级外壳,因为posix外壳没有进程替代。

bash手册页报告:替换
在支持命名管道(FIFO)或/ dev / fd命名打开文件的方法的系统上支持进程替换。它采用
<(列表)或>(列表)的形式。运行进程列表时,其输入或输出连接到FIFO或/ dev / fd中的某个文件。该文件的名称作为扩展的结果作为参数传递给当前命令。如果使用>(list)形式,则写入文件将为list提供输入
。如果使用<(list)形式,则应读取作为参数传递的文件以获得list的输出。
如果可用,则进程替换与参数和变量扩展,命令替换和算术
扩展。


换句话说,从实际的角度来看,可以使用以下表达式

<(commands)


作为其他需要文件作为参数的命令的文件名。或者,您可以对这样的文件使用重定向: />
如果要按顺序传递多个命令的输出,则可以使用以下形式之一:最后,如果command3接受文件参数(代替stdin),则重定向到进程替换

while read line; do something; done < <(commands)



评论


所以<()和<<()具有相同的效果,对吗?

– Solfish
17年8月17日在12:26

@solfish:不精确:可以在需要文件名的任何地方使用firse,第二个是该文件名的输入重定向

– Enzotib
17年8月24日在20:46

#4 楼

如果命令以文件列表作为参数并将这些文件作为输入(或输出,但不常见)进行处理,则这些文件中的每一个都可以是命名管道或/ dev / fd伪文件,由进程替代透明提供: />
$ sort -m <(command1) <(command2) <(command3)


这将“传递”三个命令的输出进行排序,因为sort可以在命令行上获取输入文件的列表。

评论


IIRC <(命令)语法仅用于bash功能。

–幽灵
2011年7月21日在8:23

@Philomath:它也在ZSH中。

–卡莱布
2011年7月21日在9:37

好吧,ZSH拥有一切...(或至少尝试做到)。

–幽灵
2011年7月21日在9:54

@Philomath:在其他shell中如何实现进程替换?

– camh
2011年7月21日在12:23

与许多高级Shell功能一样,@​​Philomath <()最初是ksh功能,被bash和zsh所采用。 psub专门是鱼功能,与POSIX无关。

–吉尔斯'所以-不再是邪恶的'
2011年7月21日在16:23



#5 楼

应当注意,进程替换不限于<(command)的形式,它使用command的输出作为文件。它的格式可以是>(command),也可以将文件作为输入输入到command。在@enzotib的答案中,bash手册的引用中也提到了这一点。 />
date > >(cat)


请注意,date | cat之前的>(command)是必要的。 >可以再次用@Caleb的答案清楚地说明这一点。到stderr。

假定您有一个仅将文件名作为参数并且不处理>(cat)echo的程序。我将使用过于简化的脚本>进行说明。 date >(cat)的内容是

$ echo >(cat)
/dev/fd/63


基本上,它测试其两个参数都是文件(不一定是常规文件),如果是这种情况,则写第一个字段使用awk对date /dev/fd/63stdin的每一行进行运算。然后,将到目前为止所提到的所有内容组合在一起的命令是:

并且等效于

#!/bin/bash
[ -e "" -a -e "" ] && awk '{print }' "" > ""


,但以下操作无效,我们必须在此处使用过程替换,
或其等效形式

./psub.sh <(printf "a a\nc c\nb b") >(sort)


如果stdout除上述内容外还读取psub.sh,则不存在这种等效形式除了流程替代,我们什么都不能使用(当然,您也可以使用命名管道或临时文件,但这是另一回事了。)