假设我们有一个像这样的 bash 脚本:
echo "x" &
echo "y" &
echo "z" &
.....
echo "Z" &
wait
有没有办法收集子外壳/子进程的退出代码?正在寻找方法来做到这一点,但找不到任何东西。我需要并行运行这些子 shell,否则会更容易。
我正在寻找一个通用的解决方案(我有未知/动态数量的并行运行的子进程)。
答案1
使用wait
带有 PID,其中将要:
等待每个进程ID指定的子进程PID或工作规范工作规范退出并返回等待的最后一个命令的退出状态。
您需要随时保存每个进程的 PID:
echo "x" & X=$!
echo "y" & Y=$!
echo "z" & Z=$!
您还可以在脚本中启用作业控制set -m
并使用%n
作业规范,但您几乎肯定不想 -工作控制还有很多其他副作用。
wait
将返回与进程完成时相同的代码。您可以wait $X
在以后的任何(合理的)时间点使用 as 来访问最终代码,$?
或者简单地将其用作 true/false:
echo "x" & X=$!
echo "y" & Y=$!
...
wait $X
echo "job X returned $?"
wait
如果命令尚未完成,则会暂停直到命令完成。
如果你想避免这样的停滞,你可以设置为trap
开SIGCHLD
,计算终止次数,并wait
在它们全部完成后立即处理所有 s。您wait
几乎可以一直单独使用。
答案2
Alexander Mills 使用handleJobs 的答案给了我一个很好的起点,但也给了我这个错误
警告:run_pending_traps:trap_list[17] 中的值错误:0x461010
相反,我只是存储每个孩子的 pid,然后等待并专门获取每个孩子的退出代码。我发现这种更干净的方式是子进程在函数中生成子进程,并避免了等待父进程的风险,而我本想等待子进程。因为没有使用陷阱,所以发生的事情更清楚。
#!/usr/bin/env bash
# it seems it does not work well if using echo for function return value, and calling inside $() (is a subprocess spawned?)
function wait_and_get_exit_codes() {
children=("$@")
EXIT_CODE=0
for job in "${children[@]}"; do
echo "PID => ${job}"
CODE=0;
wait ${job} || CODE=$?
if [[ "${CODE}" != "0" ]]; then
echo "At least one test failed with exit code => ${CODE}" ;
EXIT_CODE=1;
fi
done
}
DIRN=$(dirname "$0");
commands=(
"{ echo 'a'; exit 1; }"
"{ echo 'b'; exit 0; }"
"{ echo 'c'; exit 2; }"
)
clen=`expr "${#commands[@]}" - 1` # get length of commands - 1
children_pids=()
for i in `seq 0 "$clen"`; do
(echo "${commands[$i]}" | bash) & # run the command via bash in subshell
children_pids+=("$!")
echo "$i ith command has been issued as a background job"
done
# wait; # wait for all subshells to finish - its still valid to wait for all jobs to finish, before processing any exit-codes if we wanted to
#EXIT_CODE=0; # exit code of overall script
wait_and_get_exit_codes "${children_pids[@]}"
echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end
答案3
我认为其他答案是不必要的复杂。
如果您在启动后台 pid 时将其保存在数组中,则可以显式等待它们并在第二个数组中收集返回代码:
pids=()
echo "x" & pids+=($!)
echo "y" & pids+=($!)
echo "z" & pids+=($!)
....
echo "Z" & pids+=($!)
# wait and collect return codes
rets=()
for pid in ${pids[*]}; do
wait $pid
rets+=($?)
done
echo "Return codes: ${rets[*]}"
收集返回码以相同的顺序随着工作岗位的推出。
如果您只需要知道一项或多项作业是否失败,则无需收集所有返回代码:
error=false
for pid in ${pids[*]}; do
if ! wait $pid; then
error=true
fi
done
答案4
如果您有一种识别命令的好方法,您可以将其退出代码打印到 tmp 文件,然后访问您感兴趣的特定文件:
#!/bin/bash
for i in `seq 1 5`; do
( sleep $i ; echo $? > /tmp/cmd__${i} ) &
done
wait
for i in `seq 1 5`; do # or even /tmp/cmd__*
echo "process $i:"
cat /tmp/cmd__${i}
done
不要忘记删除 tmp 文件。