tko*_*zka 518 bash process wait
如何在bash脚本中等待从该脚本生成的几个子进程完成并返回退出代码!= 0当任何子进程以代码结束时!= 0?
简单的脚本:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
Run Code Online (Sandbox Code Playgroud)
上面的脚本将等待所有10个生成的子进程,但它总是会给出退出状态0(请参阅参考资料help wait
).如何修改此脚本,以便它发现生成的子进程的退出状态,并在任何子进程以代码!= 0结束时返回退出代码1?
有没有更好的解决方案,而不是收集子流程的PID,按顺序等待它们并总结退出状态?
Luc*_*nti 471
wait
也(可选)使进程的PID等待,并使用$!你得到在后台启动的最后一个命令的PID.修改循环以将每个生成的子进程的PID存储到数组中,然后再次循环等待每个PID.
# run processes and store pids in array
for i in $n_procs; do
./procs[${i}] &
pids[${i}]=$!
done
# wait for all pids
for pid in ${pids[*]}; do
wait $pid
done
Run Code Online (Sandbox Code Playgroud)
Hov*_*ell 273
http://jeremy.zawodny.com/blog/archives/010717.html:
#!/bin/bash
FAIL=0
echo "starting"
./sleeper 2 0 &
./sleeper 2 1 &
./sleeper 3 0 &
./sleeper 2 0 &
for job in `jobs -p`
do
echo $job
wait $job || let "FAIL+=1"
done
echo $FAIL
if [ "$FAIL" == "0" ];
then
echo "YAY!"
else
echo "FAIL! ($FAIL)"
fi
Run Code Online (Sandbox Code Playgroud)
Ole*_*nge 47
如果你安装了GNU Parallel,你可以这样做:
# If doCalculations is a function
export -f doCalculations
seq 0 9 | parallel doCalculations {}
Run Code Online (Sandbox Code Playgroud)
GNU Parallel将为您提供退出代码:
0 - 所有作业都运行无误.
1-253 - 部分工作失败.退出状态提供失败作业的数量
254 - 超过253个职位失败.
255 - 其他错误.
ken*_*orb 47
这是一个简单的例子wait
.
运行一些流程:
$ sleep 10 &
$ sleep 10 &
$ sleep 20 &
$ sleep 20 &
Run Code Online (Sandbox Code Playgroud)
然后用wait
命令等待它们:
$ wait < <(jobs -p)
Run Code Online (Sandbox Code Playgroud)
或者只是wait
(没有参数)所有人.
这将等待后台中的所有作业完成.
如果提供该-n
选项,则等待下一个作业终止并返回其退出状态.
请参阅:help wait
和help jobs
语法.
但缺点是,这将仅返回最后一个ID的状态,因此您需要检查每个子进程的状态并将其存储在变量中.
或者让你的计算函数在失败时创建一些文件(空或失败日志),然后检查该文件是否存在,例如
$ sleep 20 && true || tee fail &
$ sleep 20 && false || tee fail &
$ wait < <(jobs -p)
$ test -f fail && echo Calculation failed.
Run Code Online (Sandbox Code Playgroud)
pat*_*_ai 41
怎么样简单:
#!/bin/bash
pids=""
for i in `seq 0 9`; do
doCalculations $i &
pids="$pids $!"
done
wait $pids
...code continued here ...
Run Code Online (Sandbox Code Playgroud)
更新:
正如多个评论者指出的那样,上面等待所有进程在继续之前完成,但如果其中一个失败则不会退出和失败,可以使用@Bryan,@ SamBrightman和其他人建议的以下修改来完成:
#!/bin/bash
pids=""
RESULT=0
for i in `seq 0 9`; do
doCalculations $i &
pids="$pids $!"
done
for pid in $pids; do
wait $pid || let "RESULT=1"
done
if [ "$RESULT" == "1" ];
then
exit 1
fi
...code continued here ...
Run Code Online (Sandbox Code Playgroud)
Mar*_*gar 38
这是我到目前为止所提出的.我想看看如果一个孩子终止,如何中断睡眠命令,这样就不必调整WAITALL_DELAY
到一个人的用法.
waitall() { # PID...
## Wait for children to exit and indicate whether all exited with 0 status.
local errors=0
while :; do
debug "Processes remaining: $*"
for pid in "$@"; do
shift
if kill -0 "$pid" 2>/dev/null; then
debug "$pid is still alive."
set -- "$@" "$pid"
elif wait "$pid"; then
debug "$pid exited with zero exit status."
else
debug "$pid exited with non-zero exit status."
((++errors))
fi
done
(("$#" > 0)) || break
# TODO: how to interrupt this sleep when a child terminates?
sleep ${WAITALL_DELAY:-1}
done
((errors == 0))
}
debug() { echo "DEBUG: $*" >&2; }
pids=""
for t in 3 5 4; do
sleep "$t" &
pids="$pids $!"
done
waitall $pids
Run Code Online (Sandbox Code Playgroud)
nob*_*bar 19
为了并行化......
for i in $(whatever_list) ; do
do_something $i
done
Run Code Online (Sandbox Code Playgroud)
把它翻译成这个......
for i in $(whatever_list) ; do echo $i ; done | ## execute in parallel...
(
export -f do_something ## export functions (if needed)
export PATH ## export any variables that are required
xargs -I{} --max-procs 0 bash -c ' ## process in batches...
{
echo "processing {}" ## optional
do_something {}
}'
)
Run Code Online (Sandbox Code Playgroud)
--max-procs
根据所需的并行度进行设置(0
意思是"一次性").xargs
- 但默认情况下并不总是安装.for
在这个例子中,循环并不是绝对必要的,因为echo $i
它基本上只是重新生成输出$(whatever_list
.我只是认为使用for
关键字可以更容易地看到发生了什么.这是一个简化的工作示例......
for i in {0..5} ; do echo $i ; done |xargs -I{} --max-procs 2 bash -c '
{
echo sleep {}
sleep 2s
}'
Run Code Online (Sandbox Code Playgroud)
Gab*_*les 12
这是@Luca Tettamanti 得到最多支持的答案的扩展,以制作一个完全可运行的示例。
这个答案让我想知道:
变量是什么类型
n_procs
,它包含什么?变量是什么类型procs
,它包含什么?有人可以通过添加这些变量的定义来更新此答案以使其可运行吗?我不明白怎么办。
...并且:
无论如何,我已经弄清楚了,所以这是一个完全可运行的示例。
笔记:
$!
就是如何获取最后执行的子进程的PID(进程ID)。&
运行任何带有其后的命令cmd &
(例如 ),都会使其作为与主进程并行的子进程在后台运行。myarray=()
是如何在 bash 中创建数组。wait
要了解有关内置命令的更多信息,请参阅help wait
。另请参阅有关 Job Control 内置程序的官方 Bash 用户手册wait
,例如和jobs
,此处: https: //www.gnu.org/software/bash/manual/html_node/Job-Control-Builtins.html#索引等待。multi_process_program.sh(来自我的eRCaGuy_hello_world存储库):
#!/usr/bin/env bash
# This is a special sleep function which returns the number of seconds slept as
# the "error code" or return code" so that we can easily see that we are in
# fact actually obtaining the return code of each process as it finishes.
my_sleep() {
seconds_to_sleep="$1"
sleep "$seconds_to_sleep"
return "$seconds_to_sleep"
}
# Create an array of whatever commands you want to run as subprocesses
procs=() # bash array
procs+=("my_sleep 5")
procs+=("my_sleep 2")
procs+=("my_sleep 3")
procs+=("my_sleep 4")
num_procs=${#procs[@]} # number of processes
echo "num_procs = $num_procs"
# run commands as subprocesses and store pids in an array
pids=() # bash array
for (( i=0; i<"$num_procs"; i++ )); do
echo "cmd = ${procs[$i]}"
${procs[$i]} & # run the cmd as a subprocess
# store pid of last subprocess started; see:
# https://unix.stackexchange.com/a/30371/114401
pids+=("$!")
echo " pid = ${pids[$i]}"
done
# OPTION 1 (comment this option out if using Option 2 below): wait for all pids
for pid in "${pids[@]}"; do
wait "$pid"
return_code="$?"
echo "PID = $pid; return_code = $return_code"
done
echo "All $num_procs processes have ended."
Run Code Online (Sandbox Code Playgroud)
通过运行将上面的文件更改为可执行文件chmod +x multi_process_program.sh
,然后像这样运行它:
time ./multi_process_program.sh
Run Code Online (Sandbox Code Playgroud)
样本输出。查看调用中命令的输出如何time
显示运行时间为 5.084 秒。我们还能够成功地从每个子流程检索返回代码。
Run Code Online (Sandbox Code Playgroud)eRCaGuy_hello_world/bash$ time ./multi_process_program.sh num_procs = 4 cmd = my_sleep 5 pid = 21694 cmd = my_sleep 2 pid = 21695 cmd = my_sleep 3 pid = 21697 cmd = my_sleep 4 pid = 21699 PID = 21694; return_code = 5 PID = 21695; return_code = 2 PID = 21697; return_code = 3 PID = 21699; return_code = 4 All 4 processes have ended. PID 21694 is done; return_code = 5; 3 PIDs remaining. PID 21695 is done; return_code = 2; 2 PIDs remaining. PID 21697 is done; return_code = 3; 1 PIDs remaining. PID 21699 is done; return_code = 4; 0 PIDs remaining. real 0m5.084s user 0m0.025s sys 0m0.061s
如果您想在每个进程完成时执行某些操作,并且不知道它们何时完成,则可以在无限循环中轮询while
以查看每个进程何时终止,然后执行您想要的任何操作。
只需注释掉上面的“OPTION 1”代码块,并将其替换为“OPTION 2”块:
eRCaGuy_hello_world/bash$ time ./multi_process_program.sh
num_procs = 4
cmd = my_sleep 5
pid = 21694
cmd = my_sleep 2
pid = 21695
cmd = my_sleep 3
pid = 21697
cmd = my_sleep 4
pid = 21699
PID = 21694; return_code = 5
PID = 21695; return_code = 2
PID = 21697; return_code = 3
PID = 21699; return_code = 4
All 4 processes have ended.
PID 21694 is done; return_code = 5; 3 PIDs remaining.
PID 21695 is done; return_code = 2; 2 PIDs remaining.
PID 21697 is done; return_code = 3; 1 PIDs remaining.
PID 21699 is done; return_code = 4; 0 PIDs remaining.
real 0m5.084s
user 0m0.025s
sys 0m0.061s
Run Code Online (Sandbox Code Playgroud)
注释掉选项 1 并使用选项 2 的完整程序的示例运行和输出:
Run Code Online (Sandbox Code Playgroud)eRCaGuy_hello_world/bash$ ./multi_process_program.sh num_procs = 4 cmd = my_sleep 5 pid = 22275 cmd = my_sleep 2 pid = 22276 cmd = my_sleep 3 pid = 22277 cmd = my_sleep 4 pid = 22280 PID 22276 is done; return_code = 2; 3 PIDs remaining. PID 22277 is done; return_code = 3; 2 PIDs remaining. PID 22280 is done; return_code = 4; 1 PIDs remaining. PID 22275 is done; return_code = 5; 0 PIDs remaining.
PID XXXXX is done
该进程终止后,每一行都会立即打印出来!请注意,即使sleep 5
(在本例中为 PID 22275
)的进程首先运行,但它最后完成,并且我们在每个进程终止后立即成功检测到它。我们还成功检测到每个返回代码,就像选项 1 中一样。
*****+ [非常有帮助]获取后台进程的退出代码- 这个答案教会了我关键原则(强调):
wait <n>
等待具有 PID 的进程完成(它将阻塞直到进程完成,因此在确定进程完成之前您可能不想调用此函数),然后返回已完成进程的退出代码。
换句话说,它帮助我知道即使在该过程完成后,您仍然可以调用wait
它来获取其返回码!
从 Bash 数组中删除元素- 请注意,bash 数组中的元素实际上并未被删除,它们只是“未设置”。请参阅上面代码中我的注释,了解这意味着什么。
如何使用命令行可执行文件true
在 bash 中创建无限 while 循环:https://www.cyberciti.biz/faq/bash-infinite-loop/
小智 11
这是我使用的东西:
#wait for jobs
for job in `jobs -p`; do wait ${job}; done
Run Code Online (Sandbox Code Playgroud)
我不相信Bash的内置功能是可能的.
您可以在孩子退出时收到通知:
#!/bin/sh
set -o monitor # enable script job control
trap 'echo "child died"' CHLD
Run Code Online (Sandbox Code Playgroud)
但是,没有明显的方法可以让孩子在信号处理程序中退出状态.
获取子级状态通常wait
是较低级别POSIX API中的函数族的工作.不幸的是,Bash对此的支持是有限的 - 您可以等待一个特定的子进程(并获得其退出状态),或者您可以等待所有这些进程,并始终获得0结果.
看起来不可能做的是相当于waitpid(-1)
,阻塞直到任何子进程返回.
我看到很多很好的例子列在这里,也想把它扔进去.
#! /bin/bash
items="1 2 3 4 5 6"
pids=""
for item in $items; do
sleep $item &
pids+="$! "
done
for pid in $pids; do
wait $pid
if [ $? -eq 0 ]; then
echo "SUCCESS - Job $pid exited with a status of $?"
else
echo "FAILED - Job $pid exited with a status of $?"
fi
done
Run Code Online (Sandbox Code Playgroud)
我使用非常类似于并行启动/停止服务器/服务的东西并检查每个退出状态.对我来说很棒.希望这可以帮助别人!
如果您有 bash 4.2 或更高版本可用,以下内容可能对您有用。它使用关联数组来存储任务名称及其“代码”以及任务名称及其 pid。我还内置了一个简单的速率限制方法,如果您的任务消耗大量 CPU 或 I/O 时间并且您想限制并发任务的数量,它可能会派上用场。
该脚本在第一个循环中启动所有任务并在第二个循环中使用结果。
对于简单的情况,这有点矫枉过正,但它允许非常整洁的东西。例如,可以将每个任务的错误消息存储在另一个关联数组中,并在一切稳定后打印它们。
#! /bin/bash
main () {
local -A pids=()
local -A tasks=([task1]="echo 1"
[task2]="echo 2"
[task3]="echo 3"
[task4]="false"
[task5]="echo 5"
[task6]="false")
local max_concurrent_tasks=2
for key in "${!tasks[@]}"; do
while [ $(jobs 2>&1 | grep -c Running) -ge "$max_concurrent_tasks" ]; do
sleep 1 # gnu sleep allows floating point here...
done
${tasks[$key]} &
pids+=(["$key"]="$!")
done
errors=0
for key in "${!tasks[@]}"; do
pid=${pids[$key]}
local cur_ret=0
if [ -z "$pid" ]; then
echo "No Job ID known for the $key process" # should never happen
cur_ret=1
else
wait $pid
cur_ret=$?
fi
if [ "$cur_ret" -ne 0 ]; then
errors=$(($errors + 1))
echo "$key (${tasks[$key]}) failed."
fi
done
return $errors
}
main
Run Code Online (Sandbox Code Playgroud)
#!/bin/bash
set -m
for i in `seq 0 9`; do
doCalculations $i &
done
while fg; do true; done
Run Code Online (Sandbox Code Playgroud)
set -m
允许您在脚本中使用 fg 和 bgfg
,除了将最后一个进程置于前台外,还具有与其前台进程相同的退出状态while fg
fg
当任何退出状态非零时将停止循环不幸的是,当后台进程以非零退出状态退出时,这不会处理这种情况。(循环不会立即终止。它将等待前面的进程完成。)
如果任何doCalculations失败,以下代码将等待所有计算的完成并返回退出状态1 .
#!/bin/bash
for i in $(seq 0 9); do
(doCalculations $i >&2 & wait %1; echo $?) &
done | grep -qv 0 && exit 1
Run Code Online (Sandbox Code Playgroud)
只需将结果存储在shell中,例如存储在文件中.
#!/bin/bash
tmp=/tmp/results
: > $tmp #clean the file
for i in `seq 0 9`; do
(doCalculations $i; echo $i:$?>>$tmp)&
done #iterate
wait #wait until all ready
sort $tmp | grep -v ':0' #... handle as required
Run Code Online (Sandbox Code Playgroud)
小智 5
我已经尝试过并结合了此处其他示例中的所有最佳部分。该脚本将checkpids
在任何后台进程退出时执行该函数,并输出退出状态,而无需轮询。
#!/bin/bash
set -o monitor
sleep 2 &
sleep 4 && exit 1 &
sleep 6 &
pids=`jobs -p`
checkpids() {
for pid in $pids; do
if kill -0 $pid 2>/dev/null; then
echo $pid is still alive.
elif wait $pid; then
echo $pid exited with zero exit status.
else
echo $pid exited with non-zero exit status.
fi
done
echo
}
trap checkpids CHLD
wait
Run Code Online (Sandbox Code Playgroud)
这是我的版本,适用于多个pid,如果执行时间过长则记录警告,如果执行时间超过给定值,则停止子进程.
function WaitForTaskCompletion {
local pids="${1}" # pids to wait for, separated by semi-colon
local soft_max_time="${2}" # If execution takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
local hard_max_time="${3}" # If execution takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
local caller_name="${4}" # Who called this function
local exit_on_error="${5:-false}" # Should the function exit program on subprocess errors
Logger "${FUNCNAME[0]} called by [$caller_name]."
local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script
local exec_time=0 # Seconds since the beginning of this function
local retval=0 # return value of monitored pid process
local errorcount=0 # Number of pids that finished with errors
local pidCount # number of given pids
IFS=';' read -a pidsArray <<< "$pids"
pidCount=${#pidsArray[@]}
while [ ${#pidsArray[@]} -gt 0 ]; do
newPidsArray=()
for pid in "${pidsArray[@]}"; do
if kill -0 $pid > /dev/null 2>&1; then
newPidsArray+=($pid)
else
wait $pid
result=$?
if [ $result -ne 0 ]; then
errorcount=$((errorcount+1))
Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]."
fi
fi
done
## Log a standby message every hour
exec_time=$(($SECONDS - $seconds_begin))
if [ $((($exec_time + 1) % 3600)) -eq 0 ]; then
if [ $log_ttime -ne $exec_time ]; then
log_ttime=$exec_time
Logger "Current tasks still running with pids [${pidsArray[@]}]."
fi
fi
if [ $exec_time -gt $soft_max_time ]; then
if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
Logger "Max soft execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]."
soft_alert=1
SendAlert
fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
Logger "Max hard execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]. Stopping task execution."
kill -SIGTERM $pid
if [ $? == 0 ]; then
Logger "Task stopped successfully"
else
errrorcount=$((errorcount+1))
fi
fi
fi
pidsArray=("${newPidsArray[@]}")
sleep 1
done
Logger "${FUNCNAME[0]} ended for [$caller_name] using [$pidCount] subprocesses with [$errorcount] errors."
if [ $exit_on_error == true ] && [ $errorcount -gt 0 ]; then
Logger "Stopping execution."
exit 1337
else
return $errorcount
fi
}
# Just a plain stupid logging function to replace with yours
function Logger {
local value="${1}"
echo $value
}
Run Code Online (Sandbox Code Playgroud)
例如,等待所有三个进程完成,如果执行时间超过5秒则记录警告,如果执行时间超过120秒则停止所有进程.不要在失败时退出程序.
function something {
sleep 10 &
pids="$!"
sleep 12 &
pids="$pids;$!"
sleep 9 &
pids="$pids;$!"
WaitForTaskCompletion $pids 5 120 ${FUNCNAME[0]} false
}
# Launch the function
someting
Run Code Online (Sandbox Code Playgroud)
等待所有作业并返回最后一个失败作业的退出代码。与上述解决方案不同,这不需要保存 pid 或修改脚本的内部循环。只是离开,然后等待。
function wait_ex {
# this waits for all jobs and returns the exit code of the last failing job
ecode=0
while true; do
[ -z "$(jobs)" ] && break
wait -n
err="$?"
[ "$err" != "0" ] && ecode="$err"
done
return $ecode
}
Run Code Online (Sandbox Code Playgroud)
编辑:修复了可能被运行不存在命令的脚本所愚弄的错误。
归档时间: |
|
查看次数: |
477284 次 |
最近记录: |