惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
D
Docker
有赞技术团队
有赞技术团队
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
爱范儿
爱范儿
H
Help Net Security
美团技术团队
MyScale Blog
MyScale Blog
B
Blog RSS Feed
C
Check Point Blog
Microsoft Security Blog
Microsoft Security Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
G
Google Developers Blog
月光博客
月光博客
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs

Oskyla 烹茶室

修复 Joplin on KDE 菜单栏显示问题 Copy Fail:Linux 内核 2017 年至今的高危漏洞(附临时缓解方案) | CVE-2026-31431 Hermes Agent — 在 K3s / K8s 中运行指南 在 K3s 节点上安装并使用 nerdctl Mouser:轻量开源的罗技鼠标驱动替代方案 Claude Opus 4.7:优缺点与评测信息汇总 openFuyao NPU-Operator故障排查 openFuyao 2603 共测测试报告 openFuyao InferNex AI推理集成部署 310P(300I Pro) 环境问题记录及解决 ceph mon Operation not permitted 问题解决 Ascend 310P + openFuyao + NPU-Operator 故障排查 KDE Plasma6 禁用全局菜单,恢复正常应用菜单 终极指南:在 Linux 裸机服务器上快速部署 Moltbot (原 Clawbot) 并集成飞书 Windows 配置 Claude Code 解决 settings.json 不生效 Windows 配置 Claude Code 全流程 2025-12-31 | 年终总结 AI 生图精品提示词|第二期:城市星球 AI 生图精品提示词|第一期 Kubernetes kubectl --raw 使用指南 彻底解决阿里云和 tailscale 冲突 2025-10-21 | 沉淀思维 macOS 单独为鼠标或触控板开启自然滚动 2025-10-16 | 负载高低 2025-10-15 | 睡眠周期 2025-10-14 | 转换情绪与独立观点 go 拉取 gitcode.com 私有 mod Git 将某个文件恢复到其他分支的状态 SSH 通过跳板机连接 lxc 使用 chronyc 构建 ntp 服务 2025-10-13 | 独立思考于未来能源
对 tail -f 使用管道
Tianlun Song · 2025-02-27 · via Oskyla 烹茶室

本文 首发于 🌱 煎茶转载 请注明 来源

来源: 对 tail -f 使用管道

最近发现 tail -f 时管道后面的程序都会被卡住,才发现 grep,sed,awk 不直接输出到 TTY 时都是带缓冲的。平时跟在 cat 后使用没问题是因为输入管道关闭触发了 flush。本文详细解释其中的坑,以及怎么让 sed, awk, grep 立即 flush。

TL;DR:grep 添加 --line-buffered,sed 添加 -u,awk 调 fflush()

管道和缓冲

管道 是 Linux/Unix 中进程间通信的一种方式,可以在命令间、进程间传递数据。比如下面的命令用来来打印所有文件不存在的异常。

cat log.txt | grep Error | grep ENOENT

由于 cat 命令会在读完文件后立即退出并关闭 STDOUT,grep 的缓冲会立即 flush,我们会在执行完上述命令后立即看到输出。但如果改成实时打印日志的 tail -f 则会看不到任何输出:

tail -f log.txt | grep Error | grep ENOENT

因为当 grep 的输出不是 TTY(终端) 时,会启用缓冲。输入关闭或缓冲区满时才输出。这个例子中第一个 grep 的输入 tail -f 一直没有关闭,因此缓冲一直不会输出,第二个 grep 也永远不会收到输入。 因此控制台不会有任何输出。

但如果反过来,grep 的输出是 TTY 时就不会缓冲。也就是说 tail -f log.txt | grep Error(注意少了一个 grep)会正常地持续地输出。

检查输出文件

那么 grep 会检查它输出到哪里?虽然理论上有悖于管道的设计,也不那么函数式。 难以想象我们有个函数,它的返回值竟然会取决于这个返回值下一步被用于做什么操作。 不仅是 grep,sed 也有类似的行为,这里不去更多地讨论设计,而是给几个有用的场景:

  1. 当输出到 TTY 时输出带颜色的字符,输出到文件时输出纯文本。
  2. 当输出到 TTY 时执行过程可以提示用户输入,输出到文件时则需要使用默认值或者报错。
  3. 以及 grep 的例子:输出到 TTY 时实时打印,输出到文件或其他程序时缓冲起来(因为尤其是写入磁盘文件时,没必要有输出就写)。

那么怎么判断标准输出的文件描述符呢?

注意 [ 是一个命令,-t 是它的参数,可以 man [ 查看详情。

缓冲区满

既然 tail -f 日志看不到输出是因为缓冲区没有 flush,那么缓冲区什么时候会被 flush 呢?有两种情况:

  1. 写入已经结束(类似 JavaScript 中的 Stream.prototype.end() 调用)。但是 tail -f 的输出流永远不会结束,因为 -f 会永远 follow 文件 append。作为对比,cat 命令的输出流会在读到文件尾时结束。比如执行 cat log.txt | grep Error 会立即 flush 并退出。
  2. 缓冲区满。既然叫做 Buffer 一定是有大小的,tail 写入足够多的内容后,grep 的缓冲区就会满,这时也会发生 flush。

那么 grep 的缓冲区是多大呢?既然 tail 的输出不足以填满缓冲区,我们用输出足够多的 yes 命令:

yes Error ENOENT | grep Error | grep ENOENT

yes 命令用来不断地循环(死循环,直到被 Ctrl-C)输出它的参数,因此缓冲很快会满。果然上面的命令我们可以看到大量的输出。

避免缓冲

grep 提供了 --line-buffered 来按照行缓冲,也就是每写满一行 flush 一次:

--line-buffered
     Force output to be line buffered.  By default, output is line buffered when standard output is
     a terminal and block buffered otherwise.

sed 可以用 --unbuffered 来禁用缓冲:

-u, --unbuffered
     load minimal amounts of data from the input files and flush the output buffers more often

awk 作为一门完整的编程语言,需要调用 fflush() 方法来清空缓冲:

The built-in function fflush(expr) flushes any buffered output for the file or pipe expr.

因此前面的例子中给 grep 添加 --line-buffered 即可让它持续地输出:

tail -f log.txt | grep --line-buffered Error | grep ENOENT

注意第二个 grep 不需要添加 --line-buffered,因为它的标准输出是 TTY,默认不会启用缓冲区。 下面是一个更完整的例子,从 log.txt 文件实时读日志,过滤包含 Error 的行,把 harttle 标记去掉,打印出第一列,再过滤得到 ENOENT 的行:

tail -f log.txt | grep --line-buffered Error | sed -u 's/harttle//' | awk '${print $1; fflush()}' | grep ENOENT

References