管道

管道是 Unix 哲学的核心体现:让每个程序只做一件事,做好一件事,然后通过管道把它们组合起来。管道符 | 把一个命令的输出直接传给另一个命令作为输入。

基本概念

管道的语法:

命令1 | 命令2 | 命令3 | ...

前一个命令的标准输出成为后一个命令的标准输入。

ls | grep ".txt"

这条命令把 ls 的输出传给 grep,grep 筛选出包含 ".txt" 的行。

管道的工作原理

管道在内核中创建一个缓冲区,一个进程往里面写,另一个进程从里面读。两个命令并行执行,数据流动起来。

cat large_file.txt | grep "error" | wc -l

cat 读取文件,grep 过滤包含 error 的行,wc 统计行数。三个命令同时运行,数据像流水一样经过每个处理环节。

常用管道组合

统计文件数量

ls | wc -l

查找进程

ps aux | grep nginx

分页查看

ls -l | less
cat big_file.txt | more

排序去重

sort data.txt | uniq
sort data.txt | uniq -c | sort -rn

提取特定列

df -h | awk '{print $1, $5}'
ps aux | awk '{print $1}' | sort | uniq

统计日志

cat access.log | grep "404" | wc -l
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

管道中的错误信息

管道只传递标准输出,错误信息不会通过管道:

ls / /notexist | grep "root"

/notexist 的错误信息会显示在屏幕上,不会传给 grep。如果要处理错误信息,需要先重定向:

ls / /notexist 2>&1 | grep -v "无法访问"

tee 命令

tee 命令可以同时输出到屏幕和文件,相当于管道的"三通":

ls -l | tee filelist.txt

屏幕上显示 ls 的输出,同时保存到 filelist.txt。

追加模式:

echo "新日志" | tee -a log.txt

在脚本中记录处理过程:

cat data.txt | grep "error" | tee errors.txt | wc -l

管道与变量

管道中的命令在子 Shell 中执行,变量修改不会影响外部:

count=0
cat data.txt | while read line; do
    count=$((count + 1))
done
echo "行数:$count"

运行结果:

行数:0

count 的修改丢失了。解决方法:

方法一:使用进程替换

count=0
while read line; do
    count=$((count + 1))
done < <(cat data.txt)
echo "行数:$count"

方法二:使用 lastpipe 选项

shopt -s lastpipe
count=0
cat data.txt | while read line; do
    count=$((count + 1))
done
echo "行数:$count"

方法三:用其他方式统计

count=$(wc -l < data.txt)
echo "行数:$count"

管道的性能

管道让多个命令并行执行,通常比中间文件更快:

# 使用管道
cat big.txt | grep "pattern" | sort | uniq > result.txt

# 使用中间文件
cat big.txt > temp1.txt
grep "pattern" temp1.txt > temp2.txt
sort temp2.txt > temp3.txt
uniq temp3.txt > result.txt
rm temp1.txt temp2.txt temp3.txt

管道版本更简洁,也更高效。

管道的局限性

管道传递的是文本流,不适合处理二进制数据。对于复杂的处理逻辑,可能需要用 awk 或 Python 等工具。

管道中的每个命令都是独立的进程,进程间通信有一定开销。对于简单的操作,单条命令可能更快:

# 管道方式
cat file.txt | grep "pattern"

# 单命令方式(更快)
grep "pattern" file.txt

小结

  • 管道 | 把一个命令的输出传给另一个命令
  • 管道中的命令并行执行
  • 错误信息不通过管道传递
  • tee 命令可以分流输出
  • 管道中的变量修改不会影响外部
  • 合理使用管道可以让代码更简洁高效