输入输出 (Shell 基础教程 17)

在上一节中,我们学习了如何通过管道(Pipeline)将一个命令的输出连接到下一个命令的输入。然而,这种机制主要适用于标准输入输出流。如果遇到以下场景,该怎么办?

  • 需要将两个或多个命令的输出同时链接到另一个命令?
  • 某个命令要求参数必须是文件名,但你想处理的是另一个命令生成的内容(而非实际磁盘上的文件)?

这时,进程替换(Process Substitution) 就派上用场了。

什么是进程替换?

进程替换允许使用文件名引用来代表进程的输入或输出。Shell 会创建一个命名管道(FIFO)或文件描述符,并将其路径传递给命令,使命令误以为自己在操作普通文件。

它主要有两种形式:

  • 输出替换<(cmd) —— 将命令 cmd输出作为文件路径提供给外部命令。
  • 输入替换>(cmd) —— 将命令 cmd输入作为文件路径提供给外部命令。
注意:进程替换是 Bash 和 Zsh 等高级 Shell 的特性,标准的 POSIX sh 可能不支持。

示例

作为输入文件 (使用 <(cmd))

假设您需要比较两个文件的内容。如果文件行序不一致,直接使用 diff file1 file2 可能会产生误报。通常的做法是先排序生成临时文件,然后再比较:

sort file1 > sorted_file1
sort file2 > sorted_file2
diff sorted_file1 sorted_file2

使用进程替换,您可以在一行命令中完成此操作,无需创建临时文件:

diff <(sort file1) <(sort file2)

在这里,<(sort file1) 会被替换为一个类似 /dev/fd/63 的文件路径,diff 命令会像读取普通文件一样读取 sort 命令的输出。

作为输出文件 (使用 >(cmd))

假设您希望将应用程序的日志存储到文件中,同时在控制台上打印出来。tee 命令非常适合此场景:

echo "Hello, world!" | tee /tmp/hello.txt

现在,假设您只想在文件中保存小写字符,但在输出到控制台时保留原始大小写。您可以通过进程替换将 tee 的输出重定向到另一个命令的处理流中:

echo "Hello, world!" | tee >(tr '[:upper:]' '[:lower:]' > /tmp/hello.txt)

在此例中,>(tr ...) 创建了一个文件路径,tee 将数据写入该路径,而 tr 命令则读取这些数据并进行转换后存入文件。

练习

本节没有练习。

系列教程导航


说明:本文示例基于 Bash 环境,进程替换特性不适用于所有 Shell 解释器(如 dash)。