进程替换(process substitution)是Bash中的一个有意思的语法。
语法规则:使用 <() or >() <>与(之间没有空格
很多有关Bash的文档对这条规则并没有解释得很清楚,在这里综合部分搜索后的结果,得出个人理解:
首先举一个例子,看一下进程替换做了什么 :
a> cat <(ls) #输出了当前目录下所有文件和目录的名称
b> echo <(ls) #输出/dev/fd/63
左边是比较简单的命令,cat 输出文件内容以文件的名称为参数,echo显示作为参数的字符文本。右边是使用相同的进程替换的部分<(ls),先不管<()和>()到底表示什么意思,根据执行结果的不同,容易观察并得出结论 <(ls)生成了一个名为/dev/fd/63的部分,它到底是什么?个人理解是共享文件但不是命名管道的概念,虽然/dev/fd/0 ,/dev/fd/1,/dev/fd/2有自己特定的含义。所以cat才能以文件读取它的内容并显示出来。这里需要说明的是<(ls)已经改变了ls的输出定向。因为如果此时ls的标准输出还是stdout的话,那么终端上应该是显示同样的内容两次,实际上只有一次。后面会提到,这跟>()是不同的。
注意前面提到了shell命令的参数,<() 给其它命令提供了参数。为什么这么做,难道Bash中已有重定向和管道还不够吗?广大屌丝朋友们我们在终端敲命令的时候,或者在脚本中,有的时候真的不想把命令敲太死,想有点变化,或者有什么东西除了变量扩展命令替换还能替换命令的参数。
既然<()能产生/dev/fd/63这样的文件名称,自然也能运用到重定向
while read lines ; do echo $lines ;done < <(ls)
>()是很让人误解的语法规则,GNU-Bash的文档解释说>()为()中的命令提供输入,一开始以为就是管道的概念,但两者其实是不同的。因为如果>()能改变它左边命令的输出定向的话,那么下面的命令行应该只会显示一次文本内容,反之如何
ps | tee >(cat) #同样的内容在终端上重复显示
出了什么问题呢?正如GNU-Bash文档说明的那样,<() >()类似于命令替换和变量扩展,个人猜测Bash在解释这样的命令时,首先就是创建这个“共享文件”/dev/fd/63,然后将这个文件名传给>() 左边的命令tee作为tee的参数(用来输出的文件名),然后传给右边()中的命令作为其输入命令的参数,这样实现了类似管道的功能。根据上面的例子,再举一例就彻底明白所谓的进程替换是什么概念了:
$ ps | tee > >(grep bash) #改变tee的输出定向,这里使用grep是为了确定终端的显示是由哪个命令产生的,内容输出没有重复
要满足>()语法规则的命令实际上并不多,如:
$ tee
>(wc -l>&2)< bigfile|gzip>
bigfile.gz
请参考
http://en.wikipedia.org/wiki/Process_substitution
http://blog.sanctum.geek.nz/bash-process-substitution/
http://wiki.bash-hackers.org/syntax/expansion/proc_subst
高级Bash脚本编程指南 杨春敏 黄毅