当任何子进程以代码 !=0 结束时,如何在 bash 中等待多个子进程完成并返回退出代码 !=0?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/356100/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:50:11  来源:igfitidea点击:

How to wait in bash for several subprocesses to finish and return exit code !=0 when any subprocess ends with code !=0?

bashprocesswait

提问by tkokoszka

How to wait in a bash script for several subprocesses spawned from that script to finish and return exit code !=0 when any of the subprocesses ends with code !=0 ?

当任何子进程以代码 !=0 结束时,如何在 bash 脚本中等待从该脚本产生的几个子进程完成并返回退出代码 !=0 ?

Simple script:

简单的脚本:

#!/bin/bash
for i in `seq 0 9`; do
  doCalculations $i &
done
wait

The above script will wait for all 10 spawned subprocesses, but it will always give exit status 0 (see help wait). How can I modify this script so it will discover exit statuses of spawned subprocesses and return exit code 1 when any of subprocesses ends with code !=0?

上面的脚本将等待所有 10 个产生的子进程,但它总是给出退出状态 0(请参阅 参考资料help wait)。如何修改此脚本,以便在任何子进程以代码 !=0 结束时发现生成的子进程的退出状态并返回退出代码 1?

Is there any better solution for that than collecting PIDs of the subprocesses, wait for them in order and sum exit statuses?

有没有比收集子进程的PID更好的解决方案,按顺序等待它们并总结退出状态?

采纳答案by Luca Tettamanti

waitalso (optionally) takes the PID of the process to wait for, and with $! you get the PID of the last command launched in background. Modify the loop to store the PID of each spawned sub-process into an array, and then loop again waiting on each PID.

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

回答by HoverHell

http://jeremy.zawodny.com/blog/archives/010717.html:

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

回答by kenorb

Here is simple example using wait.

这是使用wait.

Run some processes:

运行一些进程:

$ sleep 10 &
$ sleep 10 &
$ sleep 20 &
$ sleep 20 &

Then wait for them with waitcommand:

然后用wait命令等待他们:

$ wait < <(jobs -p)

Or just wait(without arguments) for all.

或者只是wait(没有参数)所有人。

This will wait for all jobs in the background are completed.

这将等待后台的所有作业完成。

If the -noption is supplied, waits for the next job to terminate and returns its exit status.

如果提供了该-n选项,则等待下一个作业终止并返回其退出状态。

See: help waitand help jobsfor syntax.

参见:help waithelp jobs语法。

However the downside is that this will return on only the status of the last ID, so you need to check the status for each subprocess and store it in the variable.

但是缺点是这只会返回最后一个 ID 的状态,因此您需要检查每个子进程的状态并将其存储在变量中。

Or make your calculation function to create some file on failure (empty or with fail log), then check of that file if exists, e.g.

或者让你的计算函数在失败时创建一些文件(空或带有失败日志),然后检查该文件是否存在,例如

$ sleep 20 && true || tee fail &
$ sleep 20 && false || tee fail &
$ wait < <(jobs -p)
$ test -f fail && echo Calculation failed.

回答by Ole Tange

If you have GNU Parallel installed you can do:

如果您安装了 GNU Parallel,您可以执行以下操作:

# If doCalculations is a function
export -f doCalculations
seq 0 9 | parallel doCalculations {}

GNU Parallel will give you exit code:

GNU Parallel 会给你退出代码:

  • 0 - All jobs ran without error.

  • 1-253 - Some of the jobs failed. The exit status gives the number of failed jobs

  • 254 - More than 253 jobs failed.

  • 255 - Other error.

  • 0 - 所有作业都运行无误。

  • 1-253 - 一些作业失败。退出状态给出了失败的作业数

  • 254 - 超过 253 个作业失败。

  • 255 - 其他错误。

Watch the intro videos to learn more: http://pi.dk/1

观看介绍视频以了解更多信息:http: //pi.dk/1

回答by patapouf_ai

How about simply:

简单地说:

#!/bin/bash

pids=""

for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

wait $pids

...code continued here ...

Update:

更新:

As pointed by multiple commenters, the above waits for all processes to be completed before continuing, but does not exit and fail if one of them fails, it can be made to do with the following modification suggested by @Bryan, @SamBrightman, and others:

正如多位评论者所指出的那样,上述内容在继续之前等待所有进程完成,但如果其中一个失败则不会退出并失败,可以通过@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 ...

回答by Mark Edgar

Here's what I've come up with so far. I would like to see how to interrupt the sleep command if a child terminates, so that one would not have to tune WAITALL_DELAYto one's usage.

这是我到目前为止所想出的。我想看看如何在孩子终止时中断 sleep 命令,这样就不必调整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

回答by nobar

To parallelize this...

为了并行化这个......

for i in $(whatever_list) ; do
   do_something $i
done

Translate it to this...

翻译成这个...

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 {}
      }' 
   )
  • If an error occursin one process, it won't interrupt the other processes, but it will result in a non-zero exit code from the sequence as a whole.
  • Exporting functions and variables may or may not be necessary, in any particular case.
  • You can set --max-procsbased on how much parallelism you want (0means "all at once").
  • GNU Paralleloffers some additional features when used in place of xargs-- but it isn't always installed by default.
  • The forloop isn't strictly necessary in this example since echo $iis basically just regenerating the output of $(whatever_list). I just think the use of the forkeyword makes it a little easier to see what is going on.
  • Bash string handling can be confusing -- I have found that using single quotes works best for wrapping non-trivial scripts.
  • You can easily interrupt the entire operation (using ^C or similar), unlike the the more direct approach to Bash parallelism.
  • 如果一个进程发生错误,它不会中断其他进程,但会导致整个序列出现非零退出代码
  • 在任何特定情况下,可能需要也可能不需要导出函数和变量。
  • 您可以--max-procs根据您想要的并行度进行设置(0意思是“一次全部”)。
  • GNU Parallel在使用时提供了一些附加功能xargs——但它并不总是默认安装。
  • for循环并非绝对必要在这个例子中,由于echo $i基本上是再生的输出$(whatever_list)。我只是认为使用for关键字可以更容易地了解正在发生的事情。
  • Bash 字符串处理可能会令人困惑——我发现使用单引号最适合包装非平凡的脚本。
  • 您可以轻松中断整个操作(使用 ^C 或类似方法),这与 Bash 并行性更直接的方法不同

Here's a simplified working example...

这是一个简化的工作示例...

for i in {0..5} ; do echo $i ; done |xargs -I{} --max-procs 2 bash -c '
   {
   echo sleep {}
   sleep 2s
   }'

回答by Alnitak

I don't believe it's possible with Bash's builtin functionality.

我不相信 Bash 的内置功能是可能的。

You canget notification when a child exits:

可以在孩子退出时收到通知:

#!/bin/sh
set -o monitor        # enable script job control
trap 'echo "child died"' CHLD

However there's no apparent way to get the child's exit status in the signal handler.

但是,没有明显的方法可以在信号处理程序中获取孩子的退出状态。

Getting that child status is usually the job of the waitfamily of functions in the lower level POSIX APIs. Unfortunately Bash's support for that is limited - you can wait for onespecific child process (and get its exit status) or you can wait for allof them, and always get a 0 result.

获取该子状态通常wait是较低级别 POSIX API 中的函数系列的工作。不幸的是,Bash 对此的支持是有限的——您可以等待一个特定的子进程(并获取其退出状态),或者您可以等待所有子进程,并且总是得到 0 结果。

What it appears impossible to do is the equivalent of waitpid(-1), which blocks until anychild process returns.

看起来不可能做的是等价于waitpid(-1), 阻塞直到任何子进程返回。

回答by Jason Slobotski

I see lots of good examples listed on here, wanted to throw mine in as well.

我看到这里列出了很多很好的例子,也想把我的也扔进去。

#! /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

I use something very similar to start/stop servers/services in parallel and check each exit status. Works great for me. Hope this helps someone out!

我使用与并行启动/停止服务器/服务非常相似的方法并检查每个退出状态。对我很有用。希望这可以帮助别人!

回答by jplozier

This is something that I use:

这是我使用的东西:

#wait for jobs
for job in `jobs -p`; do wait ${job}; done