管道符“|”的本质是将前一个命令的标准输出(stdout)作为后一个命令的标准输入(stdin),决定其能否使用、为何卡住或无输出;它不处理标准错误(stderr),且默认仅返回最后一个命令的退出码。

管道符 | 不是“把两个命令连起来”,而是把前一个命令的 stdout 当成后一个命令的 stdin —— 这个本质决定了它能不能用、为什么卡住、为什么没结果。
为什么 ps aux | grep nginx 总多出一条自身进程?
因为 grep nginx 这个进程本身也在 ps aux 的输出里,被自己匹配到了。这不是 bug,是行为符合预期。
- 安全写法一:
ps aux | grep [n]ginx—— 方括号让正则匹配单个字符,grep进程的命令行变成grep [n]ginx,不匹配nginx字串 - 安全写法二:
ps aux | grep nginx | grep -v grep—— 二次过滤掉含grep的行 - 更健壮的替代:
pgrep nginx或pidof nginx,专为查进程设计,不依赖文本匹配
哪些命令不能放在管道右边?
shell 内建命令(如 cd、export、alias)不读取 stdin,强行放右边会静默失败或报错。
-
echo "/home" | cd:无效,cd忽略输入,当前目录不变 -
echo "PATH=$PATH:/usr/local/bin" | export:无效,export不从 stdin 读变量 - 可用替代:用命令替换
cd "$(echo /home)",或用xargs:echo "/home" | xargs cd(注意:xargs 启动新子 shell,cd 后仍会退出,实际切换无效;真正要用需echo "/home" | xargs -I {} bash -c 'cd {}; pwd')
管道中途出错却显示“成功”,怎么让脚本真正失败?
默认情况下,cmd1 | cmd2 | cmd3 只返回 cmd3 的退出码。哪怕 cmd1 找不到文件报错,整个管道也返回 0。
- 加
set -o pipefail到脚本开头,整条管道任一环节非零退出,就整体失败 - 但注意:
ls *.log 2>/dev/null | head -1这类场景本意就是忽略ls的 no-match 错误,开了pipefail反而破坏逻辑 -
pipefail在 bash/zsh 中有效,dash/sh 不支持,写跨 shell 脚本要避开
管道卡住、延迟输出,怎么强制实时刷屏?
很多命令(如 grep、sort)默认启用 full-buffer,等缓冲区满或输入结束才吐数据。配合 tail -f 这类持续输出命令时,就会卡住。
- 用
stdbuf -oL强制行缓存:tail -f /var/log/syslog | stdbuf -oL grep "error" -
grep --line-buffered是等效简写:tail -f /var/log/syslog | grep --line-buffered "error" - 注意:
awk默认行缓存,sed默认全缓存,sed -u可开启无缓冲模式
最常被忽略的是缓冲行为和退出码语义——它们不报错,却让脚本在生产环境静默失效;别只盯着语法对不对,得看数据流是不是真按你想的在走。











