工艺替代和管道


86

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

将命令的标准输出插入另一个命令的标准输入是一项强大的技术。但是,如果您需要传递多个命令的标准输出怎么办?这是过程替代的地方。

换句话说,过程替换可以执行管道的任何操作吗?

进程替换可以做什么,而管道不能做什么?

Answers:


133

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

让我们使用date命令进行测试。

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

这是一个毫无意义的示例,但是它表明cat接受了dateSTDIN上的输出并将其吐出。通过流程替换可以达到相同的结果:

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

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

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

cat收到文件名时,它将为我们读取文件的内容。另一方面,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

可以将进程替换(生成文件)和输入重定向(将文件连接到STDIN)组合在一起:

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

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

$ echo < <(date)
<blank>

由于echo不会读取STDIN且未传递任何参数,因此我们什么也没有。

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


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

我知道这个答案是合法的“因为它使用单词神交 :d
AQN

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

24

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

bash 手册页报告:

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

如果可用,进程替换将与参数和变量扩展,命令替换和算术扩展同时执行。

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

<(commands)

作为其他需要文件作为参数的命令的文件名。或者,您可以对此类文件使用重定向:

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

回到您的问题,在我看来,过程替换和管道并没有太多共同之处。

如果要按顺序传递多个命令的输出,则可以使用以下形式之一:

(command1; command2) | command3
{ command1; command2; } | command3

但您也可以在流程替换中使用重定向

command3 < <(command1; command2)

最后,如果command3接受文件参数(代替标准输入)

command3 <(command1; command2)

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

@solfish:不完美:可以在需要文件名的任何地方使用firse,第二个是该文件名的输入重定向
enzotib 17/08/24

23

这是您可以用进程替换完成的三件事,否则它们是不可能的。

多个过程输入

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

用管道根本无法做到这一点。

保留STDIN

说您有以下几点:

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)

出站流程替代

另请注意,进程替换也可以采用其他方式。因此,您可以执行以下操作:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

这是一个令人费解的示例,但是它将stdout发送到/dev/null,同时将stderr用管道传输到sed脚本以提取显示了“权限被拒绝”错误的文件的名称,然后将THOSE结果发送到文件。

请注意,第一个命令和stdout重定向位于括号(subshel​​l)中,以便仅将THAT命令的结果发送至/dev/null该命令,并且不会与其余行混淆。


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

“而管道标准错误”:不是,这是一点没有管道,而是通过FIFO文件去?
Gauthier

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

10

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

$ sort -m <(command1) <(command2) <(command3)

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


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

@Philomath:它也在ZSH中。
卡莱布

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

@Philomath:在其他shell中如何实现进程替换?
卡姆(Camh)2011年

4
<()与许多高级Shell功能一样,@Philomath 最初是ksh功能,被bash和zsh所采用。psub特别是鱼功能,与POSIX无关。
吉尔斯

3

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

对于date | cat上面的示例,使用表单的流程替换>(command)来达到相同效果的命令是,

date > >(cat)

请注意,>之前>(cat)是必要的。echo如@Caleb的答案所示,这可以再次清楚地说明。

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

因此,如果没有多余的内容>date >(cat)它将与date /dev/fd/63向stderr打印一条消息一样。

假设您有一个仅将文件名作为参数而不处理stdin或的程序stdout。我将使用过于简化的脚本psub.sh进行说明。的内容psub.sh

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

基本上,它将测试其两个参数都是文件(不一定是常规文件),如果是这种情况,请使用awk 写入"$1"to 的每一行的第一个字段"$2"。然后,将到目前为止提到的所有内容组合在一起的命令是:

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

这将打印

a
b
c

相当于

printf "a a\nc c\nb b" | awk '{print $1}' | sort

但以下操作无效,我们必须在此处使用流程替换,

printf "a a\nc c\nb b" | ./psub.sh | sort

或其等效形式

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort

如果除了上面提到的./psub.sh内容stdin之外还读取其他内容,那么就不存在这种等效形式,在这种情况下,除了进程替换之外,我们什么都不能使用(当然,您也可以使用命名管道或临时文件,但这是另一种形式)故事)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.