进程替换

进程替换是 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)

进程替换 vs 管道

管道只能单向传递数据,进程替换更灵活。

管道的局限

# 这不行,因为 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)

注意事项

Shell 兼容性

进程替换是 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/

小结

  • 进程替换把命令的输出/输入当作文件使用
  • <(命令) 输出替换,>(命令) 输入替换
  • 可以解决管道无法处理的多输入问题
  • 避免创建临时文件
  • 变量在当前 Shell 中可用
  • 是 Bash 特性,POSIX Shell 不支持