
模块S09 进程、管道与重定向篇号S09-02 / 42预计阅读45 分钟主线Bash文章目录本篇目标30 秒速览正文1. 管道是什么2. 并行不是「先跑完再跑」3. 默认退出码只看最后一个4. set -o pipefail 再探衔接 S01-045. PIPESTATUS逐节查退出码6. 与 set -e 的配合7. stderr 与管道8. 多节管道与重定向9. 读脚本时的常见模式9.1 过滤 统计9.2 检查中间步骤无 pipefail 时9.3 | while read 与退出码两个问题9.4 显式检查第一节10. 对照表11. 注意点读脚本检查清单练习判断题实操题改错题下一篇预告本篇目标掌握管道cmd1 | cmd2的数据流与退出码规则默认$?只看管道最后一节set -o pipefail为何与set -e配套S01-04。会用PIPESTATUS检查每一节读懂grep | awk、| while read等常见组合并避开「前面命令失败了脚本却继续」的坑。30 秒速览A | BA 的stdout接到 B 的stdin两命令并行执行。默认$?等于管道最后一个命令的退出码。set -o pipefail任一命令失败 →整管道视为失败与 S01-04 骨架一致。PIPESTATUS数组按顺序存管道每一节的退出码Bash。stderr 默认不进管道要合并 stderr 用21 |或 Bash|。| while read右侧常在子 shellS07-02与$?是不同问题。正文1. 管道是什么grepERROR app.log|wc-l部分含义grep ...从文件筛行结果写到stdout|把左边 stdout接到右边 stdinwc -l从 stdin 读入统计行数左边不必写文件数据流经管道可省临时文件。psaux|grepnginxcataccess.log|tail-n1002. 并行不是「先跑完再跑」yes|head-n3yes持续输出head -n 3读满 3 行后退出yes可能收到SIGPIPE被终止读脚本管道两端同时在跑读者提前结束时写者可能失败broken pipe这在set -o pipefail下会影响$?。3. 默认退出码只看最后一个false|trueecho$?# 0命令退出码false1true0管道整体$?0默认取truegrep-qNOMATCH file.txt|echookecho$?# 0 — grep 失败但 echo 成功读脚本陷阱管道前面grep、find、curl失败了只要最后一条是true、tee、wc成功默认$?仍为 0。4.set -o pipefail再探衔接 S01-04set-efalse|trueecho仍会执行# 管道整体 $? 为 0set -e 不触发set-euopipefailfalse|trueecho不会执行# 管道失败set -e 退出模式管道失败条件默认仅最后一个命令非 0pipefail任一命令非 0专栏骨架set -euo pipefail的意义之一cmd1 | cmd2 | cmd3里cmd1失败不会被cmd3的成功掩盖。关闭set o pipefail。5.PIPESTATUS逐节查退出码Bash 在管道执行后填充PIPESTATUS数组下标从 0 起false|true|falseechowhole$?echopipestatus${PIPESTATUS[*]}# whole0默认最后 true# pipestatus1 0 1set-opipefailfalse|true|falseechowhole$?# 1echo0${PIPESTATUS[0]}# 1echo1${PIPESTATUS[1]}# 0echo2${PIPESTATUS[2]}# 1注意PIPESTATUS在下一条命令执行后会被覆盖要保存就及时赋值grepfoo f|sortstatus(${PIPESTATUS[]})echogrep${status[0]}sort${status[1]}在if、里跑管道后PIPESTATUS仍可用紧接的下一条语句前。6. 与set -e的配合场景行为要点set -e 管道管道整体失败时退出失败定义见上无pipefail仅最后命令失败才使管道失败cmd || true故意吃掉失败S04-02set-euopipefailgreppattern$f|process# grep 无匹配退出 1→ 整管道失败 → 脚本退出若「无匹配不算错误」greppattern$f|process||true# 或ifgreppattern$f|process;then...7. stderr 与管道默认只有stdout进管道stderr仍去终端或外层重定向。# 只要 stdout 进 wcgrep 的错误信息仍可能在终端greppattern missing|wc-l把 stderr 也并入管道greppattern missing21|wc-lBash 还提供|同21 |cmd|other读脚本日志里「错误混进下游」时看是否有21。8. 多节管道与重定向grepERROR app.log|sort|uniq-c|sort-nr$?/PIPESTATUS对每一节都有对应下标。重定向作用在整个管道常见写法grepERROR app.log|teereport.txt|wc-l# tee 同时写文件和 stdoutwc 统计行数cmd1|cmd2out.log21# 只对整条管道末尾 cmd2 的 stdout/stderr 重定向规则见 S09-01# 更稳妥{ cmd1 | cmd2; } all.log 21复杂组合时用{ ...; }分组S09-03 子 shell 详讲。9. 读脚本时的常见模式9.1 过滤 统计awk$3 500 {print}access.log|wc-l9.2 检查中间步骤无 pipefail 时curl-fsS$url|jq.# 有 pipefailcurl 失败 → 管道失败# 无 pipefailjq 解析空输入也可能 exit 0危险9.3| while read与退出码两个问题n0grep.f|whileread-rline;do((n));doneecho$n# 子 shelln 仍为 0S07-02grep.f|sort|whileread-rline;do...;done# sort 失败 pipefail → 脚本可能退出n 的问题仍要用 f 解决9.4 显式检查第一节set-euopipefail{false;echogen;}|head-n1# 有 pipefail 时 false 导致管道失败或不用 pipefail、手动查PIPESTATUS[0]脚本片段里少见。10. 对照表问题默认pipefailfalse | true的$?01grep 无匹配 | wc的$?常为 0非 0set -e是否因左端失败退出否是查每一节PIPESTATUSPIPESTATUS11. 注意点点说明不要用$?猜第一节默认只有最后一节PIPESTATUS及时保存下一条命令会清掉SIGPIPE读者提前关闭写者可能非 0交互命令管道右端读终端时要小心少见可移植pipefail、PIPESTATUS为 Bash 等扩展纯 sh 见 S13读脚本检查清单脚本是否开了set -o pipefail或等价grep | ...左端失败是否会被掩盖是否需要21 |把错误也送进管道是否误用| while read又在循环外用累加变量检查每一节时是否用了PIPESTATUS而不是$?练习判断题A | B中A 完全结束后 B 才开始。默认情况下false | true的$?为 0。开启pipefail后false | true的$?为非 0。PIPESTATUS[0]表示管道第一个命令的退出码。管道会把左边命令的 stderr 默认送进右边。参考答案错并行读者满或退出时写者可能 SIGPIPE。对。对。对。错默认只有 stdout。实操题运行并写出$?与${PIPESTATUS[*]}Bashbash-cfalse | true; echo exit$?; echo pipe${PIPESTATUS[*]}bash-cset -o pipefail; false | true; echo exit$?; echo pipe${PIPESTATUS[*]}参考答案无 pipefailexit0pipe1 0有 pipefailexit1pipe1 0改错题#!/usr/bin/env bashset-egrepERROR$LOG|mail-salert adminexample.comechoalert sentgrep无匹配时仍打印alert sent。如何改参考set-euopipefailgrepERROR$LOG|mail-salert adminexample.comechoalert sent或业务上允许无匹配ifgrepERROR$LOG|mail-salert adminexample.com;thenechoalert sentelseechono ERROR or mail failed2fi下一篇预告S09-03《子 Shell 与命令替换()与$()》—$(cmd)与cmd、子 shell( )与管道的区别。