1. Why pipes sometimes get "stuck": buffering (jvns.ca)
管道有时会“卡住”的现象通常是由输出缓冲导致的。许多程序在通过管道或文件输出时,会先缓存数据(通常约8KB)而非立即写入,以提高性能。这会导致管道中数据延迟显示,甚至看似“无输出”。
原因:缓冲机制
- 程序会根据输出目标决定缓冲策略:若写入终端,通常使用行缓冲(逐行立即输出);若写入管道或文件,则使用块缓冲(积攒数据后批量写入)。
- 例如,在
tail -f file | grep thing1 | grep thing2中,grep thing1会因输出到管道而缓冲,可能永远积累不到8KB数据,导致后续命令无输出。
程序缓冲行为差异
- 不缓冲的命令:
tail、cat、tee。 - 常见缓冲命令及禁用缓冲的标志:
grep:--line-bufferedsed:-uawk:使用fflush()函数tcpdump:-ljq:-utr:-u
- 编程语言默认缓冲:C、Python、Ruby、Perl等语言的默认输出函数在管道中会缓冲,需通过特定设置(如Python的
-u参数)禁用。
缓冲导致的问题
- Ctrl-C 中断时数据丢失:缓冲数据可能未被刷新而丢失(如
tcpdump管道中断时)。 - 重定向到文件也会缓冲,但退出时数据通常会被写入。
解决方案
- 使用快速完成的命令:避免缓慢输出的管道(如用
cat file | grep ... | tail)。 - 添加行缓冲标志:在
grep等命令中使用--line-buffered。 - 用
awk替代多级grep:合并过滤逻辑,减少管道环节。 - 使用
stdbuf工具:通过stdbuf -o0关闭缓冲,但依赖LD_PRELOAD,兼容性有限。 - 使用
unbuffer工具:强制程序输出视为终端(少缓冲),通常可靠,但可能引入颜色等副作用。
其他注意事项
- 此现象在数据流缓慢(如
tail -f、tcpdump)时更明显。 - 理想情况下可通过环境变量全局控制缓冲,但目前无统一标准。
文章核心在于揭示缓冲对管道行为的隐形影响,并提供实用应对方法。