将命令的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流上。进程替换运行命令,将其输出保存到特殊的临时文件中,然后传递该文件名代替命令。无论您使用什么命令,都将其视为文件名。请注意,创建的文件不是常规文件,而是一个命名管道,一旦不再需要该管道便会自动删除。
#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/63
到stdin
的每一行进行运算。然后,将到目前为止所提到的所有内容组合在一起的命令是:并且等效于
#!/bin/bash
[ -e "" -a -e "" ] && awk '{print }' "" > ""
,但以下操作无效,我们必须在此处使用过程替换,
或其等效形式
./psub.sh <(printf "a a\nc c\nb b") >(sort)
如果
stdout
除上述内容外还读取psub.sh
,则不存在这种等效形式除了流程替代,我们什么都不能使用(当然,您也可以使用命名管道或临时文件,但这是另一回事了。)
评论
如果我正确理解,tldp.org / LDP / abs / html / process-sub.html#FTN.AEN18244表示进程替换会创建临时文件,而不是命名管道。据我所知,不会创建临时文件。写入管道永远不会涉及写入磁盘:stackoverflow.com/a/6977599/788700
– Adobe
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