子进程执行失败时如何退出Shell父脚本及循环
解决Shell父脚本等待所有子进程并检查退出状态的方案
这个需求在日常Shell脚本开发里挺常见的——既要混合运行前台和后台子脚本,还要确保所有子进程完成后统一检查状态,一旦有失败就立刻终止父脚本。下面我给你一套清晰的实现思路和完整代码:
核心思路
- 区分前台/后台脚本处理:
- 前台脚本:直接执行,执行完成后立即检查退出状态,失败则直接终止父脚本(同时清理已启动的后台进程)。
- 后台脚本:启动后收集其PID到数组中,等所有前台脚本执行完后,统一等待这些后台进程并逐个检查状态。
- 统一状态检查:不管是前台还是后台脚本,只要有一个执行失败(退出状态非0),父脚本最终就以失败状态退出。
完整示例代码
#!/bin/bash # 初始化存储后台进程PID的数组 declare -a pids # 可选:添加信号捕获,父脚本被中断时清理后台进程 trap 'echo "Parent script interrupted! Cleaning up background processes..."; kill "${pids[@]}" 2>/dev/null; exit 1' INT TERM # 循环遍历要执行的子脚本列表 for script in "child_foreground_1.sh" "child_background_1.sh" "child_foreground_2.sh" "child_background_2.sh"; do if [[ "$script" == *"background"* ]]; then # 处理后台运行的子脚本 ./"$script" & local pid=$! pids+=("$pid") echo "Launched background script: $script (PID: $pid)" else # 处理前台运行的子脚本 echo "Running foreground script: $script..." ./"$script" local status=$? if [[ $status -ne 0 ]]; then echo "ERROR: Foreground script $script failed with exit status $status!" echo "Cleaning up running background processes..." kill "${pids[@]}" 2>/dev/null exit $status fi echo "Foreground script $script completed successfully." fi done # 等待所有后台进程完成并检查状态 echo "Waiting for all background processes to finish..." local global_exit_status=0 for pid in "${pids[@]}"; do wait "$pid" local status=$? if [[ $status -ne 0 ]]; then echo "ERROR: Background process (PID: $pid) failed with exit status $status!" global_exit_status=$status else echo "Background process (PID: $pid) completed successfully." fi done # 根据全局状态决定父脚本退出码 if [[ $global_exit_status -ne 0 ]]; then echo "One or more scripts failed. Parent script exiting with status $global_exit_status." exit $global_exit_status fi echo "All scripts executed successfully!" exit 0
关键细节说明
- PID数组管理:用
declare -a pids声明数组,每次启动后台脚本后把$!(最近后台进程的PID)加入数组。 - 前台脚本失败处理:前台脚本执行后立即检查
$?,如果失败,先用kill "${pids[@]}"杀掉所有已启动的后台进程(2>/dev/null避免进程已结束时的报错),然后父脚本退出对应状态。 - 后台进程等待与检查:用
wait "$pid"逐个等待后台进程,wait命令会返回该进程的退出状态,我们可以直接捕获并判断。 - 信号捕获:添加
trap命令处理INT(Ctrl+C)和TERM(终止信号),避免父脚本被中断后留下孤儿进程。
注意事项
- 确保所有子脚本在执行失败时正确返回非0的退出码,否则父脚本无法识别失败情况。
- 如果你的子脚本名称没有统一的标识(比如示例中的
background关键词),可以改成用列表单独区分前台/后台脚本,比如:
然后分别遍历这两个列表处理。foreground_scripts=("child1.sh" "child3.sh") background_scripts=("child2.sh" "child4.sh")
内容的提问来源于stack exchange,提问作者Joha
相关产品推荐
相关产品推荐

