shell并发编程核心是合理使用&启动后台进程和wait控制同步点:&启动任务,wait等待完成;可指定pid或作业号按需等待;函数内wait只作用于该函数启动的后台任务;for循环中需将&置于循环内、wait置于循环外实现批量并行。

Shell并发编程的关键不是“同时开很多任务”,而是让任务该并行时并行、该等待时精准等待。核心就两个动作:& 启动后台进程,wait 控制同步点。用得好,10个串行耗时任务能压缩到最慢那个的耗时;用得乱,脚本可能提前退出、状态丢失、错误无法捕获。
基础并行:用 & 启动 + wait 收尾
这是最常用也最容易上手的模式。每个耗时操作加 & 放入后台,最后用 wait 确保全部完成再继续。
- 启动后台任务时,用
$!保存 PID 是后续精细控制的前提 -
wait不带参数,会阻塞直到当前 Shell 的所有直接子进程结束 - 注意:wait 只管“自己生的”子进程,不管孙子进程——如果某个后台任务又 fork 出新进程,wait 不会等它
按需等待:指定 PID 或作业号
当不需要等全部任务,只想等其中几个时,可以把 PID 或作业号传给 wait。
- 例如:
sleep 5 & p1=$!; sleep 8 & p2=$!; wait $p1—— 只等第一个睡醒,第二个还在跑 - 作业号(如
%1)在交互式 Shell 中更直观,脚本中建议统一用 PID,更稳定 - wait 返回的是被等待进程的退出状态码,可以用
$?检查是否成功
函数内并行:作用域决定 wait 范围
wait 的作用范围是它所在的 Shell 层级。在函数里用 wait,只等这个函数内部启动的后台任务。
- 函数外的 wait 不会等函数内启动的后台进程,除非你在函数里也加了 wait,或把函数本身放到后台再等
- 常见写法:
mytask() { sleep 3 & wait; echo "done"; }; mytask & wait - 这种嵌套等待适合模块化设计,比如每个函数负责一类子任务,主流程再统一协调
批量任务并行:for 循环 + & + wait
处理一批相似任务(如文件转换、主机探测)时,并行能显著提速。
- 写法示例:
for f in *.log; do gzip "$f" & done; wait - 注意别在循环体里直接写 wait,否则变成串行;必须把 & 放在循环体内,wait 放在循环外
- 如果任务太多,可能压垮系统,可配合有名管道或 GNU parallel 做并发数限制,但基础场景通常够用











