Section 28.1: 比较网络上的两个文件

下面使用 diff 比较两个文件,使用进程替换而不是创建临时文件。

diff <(curl http://www.example.com/page1) <(curl http://www.example.com/page2)

Section 28.2: 为 while 循环输入命令的输出结果

这将为一个 while 循环提供 grep 命令的输出结果:

while IFS=":" read -r user _
do
        # "$user" holds the username in /etc/passwd
done < <(grep "hello" /etc/passwd)

Section 28.3: 连接文件

众所周知,在同一个命令中,输入和输出不能使用同一个文件。例如

$ cat header.txt body.txt >body.txt

并不能如你所愿。当 cat 读取 body.txt 时,它已经被重定向截断,是空的。最终的结果是,body.txt 只包含 header.txt 的内容。

也许有人会认为可以通过进程替换来避免这种情况,即命令

$ cat header.txt <(cat body.txt) > body.txt

将迫使 body.txt 的原始内容在文件被重定向截断之前以某种方式保存在某个缓冲区中。这行不通。只有在所有文件描述符都设置完毕后,括号中的猫才会开始读取文件,就像外层的猫一样。在这种情况下,尝试使用进程替换是没有意义的。

将一个文件预置到另一个文件的唯一方法是创建一个中间文件:

$ cat header.txt body.txt >body.txt.new
$ mv body.txt.new body.txt

这就是 sed、perl 或类似程序在使用原文件编辑选项(通常为 -i)调用时在底层所做的工作。

Section 28.4: 通过多个程序同时流式传输文件

它可以用 wc -l 计算大文件的行数,同时用 gzip 压缩文件。两者同时运行。

tee >(wc -l >&2) < bigfile | gzip > bigfile.gz

通常,tee 会将输入写入一个或多个文件(和 stdout)。我们可以用 tee >(command)写入命令而不是文件。

在这里,命令 wc -l >&2 计数从 tee 读取的行数(而 tee 又从 bigfile 读取行数)。(行计数被发送到 stderr (>&2) 以避免与 gzip 的输入混合)。同时,tee 的 stdout 也会被送入 gzip。

Section 28.5: 使用粘贴命令

# Process substitution with paste command is common
# To compare the contents of two directories
paste <( ls /path/to/directory1 ) <( ls /path/to/directory2 )

Section 28.6: 为避免使用子shell

进程替换的一个重要方面是,它可以让我们在从 shell 输入命令时避免使用子shell。

下面举一个简单的例子来说明这一点。我的当前文件夹中有以下文件:

$ find . -maxdepth 1 -type f -print
foo bar zoo foobar foozoo barzoo

如果我用管道连接到一个 while/read 循环,该循环会递增一个计数器,如下所示:

count=0
find . -maxdepth 1 -type f -print | while IFS= read -r _; do
        ((count++))
done

现在 $count 中不包含 6,因为它是在子shell上下文中修改的。下面显示的任何命令都是在子shell上下文中运行的,在子shell终止后,其中使用的变量的作用域将丢失。

command &
command | command
( command )

进程替换可以避免使用管道操作符,从而解决这个问题。

count=0
while IFS= read -r _; do
((count++))
done < <(find . -maxdepth 1 -type f -print)

这样就可以保留计数变量的值,因为没有调用子shell。