如何在bash脚本中等待从该脚本生成的几个子进程完成并返回退出代码!= 0当任何子进程以代码结束时!= 0?
简单的脚本:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
上面的脚本将等待所有10个生成的子进程,但它总是会给出退出状态0(参见 help wait
) . 如何修改此脚本,以便发现生成的子进程的退出状态,并在任何子进程以代码!= 0结束时返回退出代码1?
有没有更好的解决方案,而不是收集子流程的PID,按顺序等待它们并汇总退出状态?
27 回答
wait也(可选)使进程的PID等待,并使用$!你得到在后台启动的最后一个命令的PID . 修改循环以将每个生成的子进程的PID存储到数组中,然后再次循环等待每个PID .
http://jeremy.zawodny.com/blog/archives/010717.html:
如果你安装了GNU Parallel,你可以这样做:
GNU Parallel将为您提供退出代码:
0 - 所有作业都运行无误 .
1-253 - 部分工作失败 . 退出状态提供失败作业的数量
254 - 超过253个职位失败 .
255 - 其他错误 .
观看介绍视频以了解更多信息:http://pi.dk/1
到目前为止,'s what I'已经提出来了 . 我想看看如果一个孩子终止,如何中断睡眠命令,这样就不必将
WAITALL_DELAY
调整为一个人的用法 .怎么样简单:
Update:
正如多个评论者指出的那样,上面等待所有进程在继续之前完成,但如果其中一个失败则不会退出和失败,可以使用@Bryan,@ SamBrightman和其他人建议的以下修改来完成:
这是使用
wait
的简单示例 .运行一些流程:
然后使用
wait
命令等待它们:或者只是等待(没有参数) .
这将等待后台中的所有作业完成 .
如果提供了-n选项,则等待下一个作业终止并返回其退出状态 .
有关语法,请参阅:
help wait
和help jobs
.但缺点是,这将仅返回最后一个ID的状态,因此您需要检查每个子进程的状态并将其存储在变量中 .
或者让你的计算函数在失败时创建一些文件(空或失败日志),然后检查该文件是否存在,例如
我需要这个,但目标进程不是当前shell的子进程,在这种情况下
wait $PID
不起作用 . 我找到了以下替代方案:这取决于 procfs 的存在,这可能不可用(例如,Mac不提供它) . 因此,为了便于携带,您可以使用它:
诱捕CHLD信号可能不起作用,因为如果它们同时到达,您可能会丢失一些信号 .
为了并行化......
把它翻译成这个......
If an error occurs 在一个进程中,它不会中断其他进程,而是 it will result in a non-zero exit code from the sequence as a whole .
在任何特定情况下,导出函数和变量可能是必需的,也可能不是必需的 .
您可以根据所需的并行度设置
--max-procs
(0
表示"all at once") .GNU Parallel在用于代替
xargs
时提供了一些附加功能 - 但默认情况下并不总是安装 .本例中并不严格需要
for
循环,因为echo $i
基本上只是重新生成$(whatever_list
的输出 . 我只是认为使用for
关键字可以更容易地看到发生了什么 .Bash字符串处理可能令人困惑 - 我发现使用单引号最适合包装非平凡的脚本 .
您可以轻松地中断整个操作(使用^ C或类似操作),unlike the the more direct approach to Bash parallelism .
这是一个简化的工作示例......
我看到这里列出了很多很好的例子,也想把它扔进去 .
我使用非常类似于并行启动/停止服务器/服务的东西并检查每个退出状态 . 对我很有用 . 希望这可以帮助别人!
我不相信Bash的内置功能是可能的 .
你 can 在孩子退出时收到通知:
但是,没有明显的方法可以让孩子在信号处理程序中退出状态 .
获得该子状态通常是较低级别POSIX API中
wait
系列函数的作用 . 不幸的是,Bash对此的支持是有限的 - 您可以等待一个特定的子进程(并获得其退出状态),或者您可以等待所有这些进程,并始终获得0结果 .看起来不可能做的是相当于
waitpid(-1)
,它阻塞直到任何子进程返回 .如果任何doCalculations失败,以下代码将等待所有计算的完成并返回退出状态1 .
只需将结果存储在shell中,例如在一个文件中 .
这是我的版本适用于多个pid,如果执行时间过长则记录警告,如果执行时间超过给定值,则停止子进程 .
例如,等待所有三个进程完成,如果执行时间超过5秒,则记录警告,停止执行时间超过120秒的所有进程 . 不要在失败时退出程序 .
如果您有bash 4.2或更高版本,则以下内容可能对您有用 . 它使用关联数组来存储任务名称及其“代码”以及任务名称及其pid . 我还内置了一个简单的速率限制方法,如果你的任务消耗大量的CPU或I / O时间并且你想要限制并发任务的数量,那么它可能会派上用场 .
该脚本在第一个循环中启动所有任务,并在第二个循环中使用结果 .
对于简单的情况,这有点矫枉过正,但它允许非常整洁的东西 . 例如,可以将每个任务的错误消息存储在另一个关联数组中,并在一切安定下来后打印它们 .
我刚刚将脚本修改为后台并将进程并行化 .
我做了一些实验(在Solaris上同时使用bash和ksh)并发现'wait'输出退出状态(如果它不为零),或者在没有提供PID参数时返回非零退出的作业列表 . 例如 .
击:
KSH:
此输出将写入stderr,因此OPs示例的简单解决方案可能是:
虽然这个:
也将返回一个计数,但没有tmp文件 . 这也可以这种方式使用,例如:
但这并不比tmp文件IMO更有用 . 我找不到一个有用的方法来避免tmp文件,同时也避免在子shell中运行“等待”,这根本不起作用 .
我已经对此进行了研究,并结合了其他示例中的所有最佳部分 . 当任何后台进程退出时,此脚本将执行
checkpids
函数,并输出退出状态而不依赖于轮询 .set -m
允许您在脚本中使用fg&bgfg
,除了将最后一个进程放在前台之外,还具有与前景进程相同的退出状态当任何
fg
以非零退出状态退出时,while fg
将停止循环遗憾的是,当后台进程以非零退出状态退出时,这将无法处理 . (循环不会立即终止 . 它将等待先前的进程完成 . )
这个工作,应该是一个好的,如果不是比@HoverHell的答案更好!
当然,我已经在NPM项目中永久化了这个脚本,它允许你并行运行bash命令,对于测试非常有用:
https://github.com/ORESoftware/generic-subshell
陷阱是你的朋友 . 您可以在许多系统中捕获ERR . 您可以捕获EXIT,或者在DEBUG上捕获每个命令后执行一段代码 .
这除了所有标准信号之外 .
顶部的
set -e
使您的脚本在失败时停止 .如果任何子作业失败,
expect
将返回1
.这里已经有很多答案,但我很惊讶似乎没有人建议使用数组......所以这就是我所做的 - 这可能对将来有些人有用 .
我最近用过这个(感谢Alnitak):
从那里可以轻松推断,并有一个触发器(触摸文件,发送信号)并更改计数标准(触摸计数文件,或其他)以响应该触发器 . 或者如果你只想要“任何”非零rc,只需从save_status中取消锁定即可 .
等待多个子进程并在其中任何一个退出非零状态代码时退出的解决方案是使用'wait -n'
状态代码'127'用于不存在的进程,这意味着孩子可能已退出 .
在等待该过程之前,可能存在过程完成的情况 . 如果我们触发等待已经完成的进程,它将触发错误,如pid不是此shell的子进程 . 为避免这种情况,可以使用以下函数来查找过程是否完成:
我认为并行运行作业并检查状态的最直接方法是使用临时文件 . 已经有几个类似的答案(例如Nietzche-jou和mug896) .
上面的代码不是线程安全的 . 如果您担心上面的代码将与其自身同时运行,最好使用更独特的文件名,例如fail . $$ . 最后一行是满足要求:“当任何子进程以代码结束时返回退出代码1!= 0?”我在那里提出了额外的要求来清理 . 它可能是更清楚地写这样:
这是一个类似的片段,用于从多个作业中收集结果:我创建一个临时目录,在单独的文件中记录所有子任务的输出,然后将它们转储以供审阅 . 这与问题并不完全匹配 - 我将其作为奖励投入:
我想也许可以运行doCalculations;在发送到后台的子shell中回显"$?" >> / tmp / acc,然后等待,然后/ tmp / acc将包含退出状态,每行一个 . 但是,我不知道附加到累加器文件的多个进程的任何后果 .
以下是对此建议的试用:
文件:doCalcualtions
档案:试试
运行./try的输出