进程替换是 Bash 的高级特性,它把命令的输出或输入当作文件来使用。这在某些场景下比管道更灵活,可以解决管道无法处理的问题。
进程替换的语法:
# 输出替换,把命令的输出当作文件
<(命令)
# 输入替换,把命令的输入当作文件
>(命令)
实际上,进程替换会创建一个临时文件(通常是 /dev/fd/63 这样的文件描述符),把命令的输出或输入连接到这个文件。
echo <(echo "hello")
cat <(echo "hello world")
运行结果:
/dev/fd/63
hello world
<(echo "hello") 创建了一个临时文件,内容是 "hello"。
diff <(ls dir1) <(ls dir2)
diff <(grep "error" log1.txt) <(grep "error" log2.txt)
这在比较两个命令输出时特别有用,不用创建临时文件。
cat <(echo "头部内容") file.txt <(echo "尾部内容")
comm <(sort file1.txt) <(sort file2.txt)
join <(sort file1.txt) <(sort file2.txt)
echo "hello" > >(cat)
输出替换用得更多,输入替换相对少见。
exec > >(tee -a log.txt)
echo "这行会同时输出到屏幕和 log.txt"
echo "data" > >(tr 'a-z' 'A-Z' > output.txt)
管道只能单向传递数据,进程替换更灵活。
# 这不行,因为 read 在子 Shell 中执行
echo "hello" | read var
echo $var
运行结果:
var 是空的,因为管道创建了子 Shell。
read var < <(echo "hello")
echo $var
运行结果:
hello
< <(echo "hello") 把命令输出当作文件,read 在当前 Shell 中执行。
管道只能有一个输入源,进程替换可以有多个:
# 比较两个命令的输出
diff <(sort file1) <(sort file2)
# 用管道做不到这一点
diff <(ls -1 dir1) <(ls -1 dir2)
diff -r <(find dir1 -type f | sort) <(find dir2 -type f | sort)
sort -m <(sort file1.txt) <(sort file2.txt) <(sort file3.txt)
comm -12 <(sort file1.txt | uniq) <(sort file2.txt | uniq)
while read line; do
echo "处理:$line"
done < <(grep "error" app.log)
变量在当前 Shell 中可用。
# 用命令生成配置的一部分
cat base.conf <(echo "# 动态配置") <(date +"# 生成时间: %Y-%m-%d %H:%M:%S") > final.conf
paste <(cut -f1 data.txt) <(cut -f3 data.txt)
# 传统方式需要临时文件
sort file1.txt > tmp1
sort file2.txt > tmp2
diff tmp1 tmp2
rm tmp1 tmp2
# 进程替换不需要
diff <(sort file1.txt) <(sort file2.txt)
进程替换是 Bash/Zsh 特性,POSIX Shell 不支持。在 sh 中会报错。
进程替换实际上是文件描述符,有些命令不支持从文件描述符读取:
# 可能失败
vim <(echo "hello")
# 解决方法:重定向到临时文件
vim <(echo "hello") # 某些版本支持
cat <(echo "hello") > temp.txt && vim temp.txt
进程替换底层使用命名管道(FIFO),可以通过以下命令查看:
echo <(echo "test")
ls -la /dev/fd/
<(命令) 输出替换,>(命令) 输入替换