Shell脚本精读 · S09-04 | 后台任务、`wait` 与进程替换

Shell脚本精读 · S09-04 | 后台任务、`wait` 与进程替换 模块S09 进程、管道与重定向篇号S09-04 / 42预计阅读45 分钟主线Bash文章目录本篇目标30 秒速览正文1. 后台执行2. wait等待结束3. 简单并行4. 脚本退出与后台任务5. 进程替换(cmd)6. while read 进程替换衔接 S07-027. (cmd) 与 tee8. 与 $()、管道的对照9. 读脚本时的常见片段9.1 并行 失败收集9.2 比较两份命令输出9.3 临时「输入文件」10. 注意点读脚本检查清单练习判断题实操题改错题S09 模块小结下一篇预告本篇目标掌握后台执行cmd 、用wait等待结束、用$!取 PID以及 Bash进程替换(cmd)/(cmd)。能写简单并行任务**读懂while read ... done (cmd)S07-02、diff (a) (b)、exec (tee ...)S09-01并避免脚本在后台任务未完成时就退出。30 秒速览cmd 后台启动$!为最近后台 PID。wait [pid]等待子进程结束无参数则等所有后台子进程。进程替换(cmd)像只读临时文件**一样把命令输出当路径用。(cmd)把写入该「路径」的内容送进命令 stdin。| while read在子 shell (cmd)常在同 shell 里readS07-02。脚本末尾要对后台任务wait否则可能被挂起或提前结束。正文1. 后台执行long_taskechostarted pid$!项说明在行末命令在后台跑Shell立刻继续下一条$!上一个后台任务的 PIDstdout/stderr默认仍可能混在终端可重定向long_tasktask.log21交互 Shell里还有jobs、fg %1、bg %1把任务调前后台脚本里更常用wait。2.wait等待结束sleep2pid$!echowaiting$pidwait$pidechodone, exit$?用法含义wait等待所有当前 shell 的子后台进程wait 1234等待指定 PIDwait $!等待刚启动的那个后台任务wait的退出码若等待的进程存在wait返回该进程的退出码PID 无效时非 0。falsewait$!echo$?# 13. 简单并行#!/usr/bin/env bashset-euopipefaildownload(){localurl$1out$2curl-fsS$url-o$out}download$URL1a.datp1$!download$URL2b.datp2$!wait$p1wait$p2echoboth done读点两个download同时跑。任一下载失败set -e下wait收到非 0 可能让脚本退出Bash 行为依版本稳妥可wait $p1 || exit 1。不要用$!覆盖前还没wait的后台 PIDcmd1pid1$!cmd2pid2$!wait$pid1wait$pid24. 脚本退出与后台任务#!/usr/bin/env bashsleep100echoscript ends# 未 wait脚本进程退出时后台 sleep 可能收到 SIGHUP 被终止# 取决于 huponexit、nohup 等可靠写法需要跑完再加waitsleep100bg$!# ... 其它工作 ...wait$bg或wait无参等全部。长时间服务用nohup/disown运维场景本篇点到为止。5. 进程替换(cmd)diff(sortfile1)(sortfile2)含义Shell 创建命名管道或/dev/fd让sort file1的输出像文件路径一样传给diff的第一个文件参数。形式方向(cmd)读cmd的 stdout像输入文件(cmd)写进cmd的 stdin像输出文件Bash 特性POSIXsh无此语法。6.while read 进程替换衔接 S07-02count0whileread-rline;do((count))done(grep.data.txt)echocount$count对比| while read右侧在子 shellcount累加不进父 shell。 (grep ...)在同一 shell 里跑循环count有效。whileread-rpid;doechopid$piddone(pgrep-fmyservice)7.(cmd)与teeS09-01预告exec(tee-arun.log)21echoalso on screen and in run.log(tee -a run.log)像「可写文件」stdout 复制进tee屏幕 文件。exec之后整条脚本 stdout 都走这条路径高级用法读脚本认得即可。8. 与$()、管道的对照机制典型用途是否「像文件路径」$(cmd)输出字符串进变量/参数否cmd1 | cmd2管道连接否(cmd)命令要文件名参数是(cmd)命令要可写文件参数是# diff 需要两个文件名不是 stdin 一条流diff(cmd1)(cmd2)# 变量赋值用 $()path$(pwd)9. 读脚本时的常见片段9.1 并行 失败收集failed0forfin$;doprocess$fpids($!)doneforpin${pids[]};dowait$p||((failed))||truedone((failed0))数组pids见 S03-04。9.2 比较两份命令输出diff-u(grep-v^#expected.conf)(grep-v^#actual.conf)9.3 临时「输入文件」tool--config(generate_config)10. 注意点点说明$!只记上一个多个后台要分别存 PIDwait与set -e后台失败应显式处理进程替换Bash 专用写可移植脚本慎用资源大量并行要限流引号wait $pid防空读脚本检查清单见到cmd 后面是否有wait多个后台是否分别保存$!( )是当文件路径传给外部命令吗需要父 shell 变量时是否用 (cmd)而非| while readexec (tee ...)是否改变全程日志去向练习判断题sleep 5 后$!是 sleep 的 PID。wait无参数会等待当前 shell 启动的所有后台子进程。(cmd)会把 cmd 的 stdout 变成可当作文件名使用的路径。diff (a) (b)在 POSIX sh 中一定可用。脚本cmd 后直接exit 0cmd 一定会跑完。参考答案对。对。对Bash 进程替换。错Bash 扩展。错父脚本退出可能导致后台被挂起或杀掉取决于环境。实操题写脚本后台各sleep 1、sleep 2、sleep 3并行启动记录三个 PIDwait全部后打印all finished。参考答案#!/usr/bin/env bashset-euopipefailsleep1p1$!sleep2p2$!sleep3p3$!wait$p1wait$p2wait$p3echoall finished或wait无参一次等齐。改错题count0grep.file|whileread-rline;do((count))doneecholines$count希望lines为行数。参考count0whileread-rline;do((count))done(grep.file)echolines$countS09 模块小结篇号主题S09-01重定向21here-docS09-02管道、pipefail、PIPESTATUSS09-03$( )命令替换、( )子 shellS09-04、wait、( )( )下一模块S10讲 Shell 与grep/sed/awk的分工与「胶水」调用。下一篇预告S10-01《何时用 Shell、何时用 awk/sed分工原则》— 文本处理选型不把所有逻辑都写进 Bash。