等待脚本中的bash后台作业完成

时间:2009-07-15 13:44:15

标签: bash scripting job-control

最大化CPU使用率(我在EC2中的Debian Lenny上运行)我有一个简单的脚本来并行启动作业:

#!/bin/bash

for i in apache-200901*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200902*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200903*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200904*.log; do echo "Processing $i ..."; do_something_important; done &
...

我对这个有效的解决方案非常满意,但是我无法弄清楚如何编写进一步的代码,只有在所有循环完成后才会执行。

有没有办法控制这个?

4 个答案:

答案 0 :(得分:76)

有一个bash内置命令。

wait [n ...]
      Wait for each specified process and return its termination  sta‐
      tus.   Each  n  may be a process ID or a job specification; if a
      job spec is given, all processes  in  that  job’s  pipeline  are
      waited  for.  If n is not given, all currently active child pro‐
      cesses are waited for, and the return  status  is  zero.   If  n
      specifies  a  non-existent  process or job, the return status is
      127.  Otherwise, the return status is the  exit  status  of  the
      last process or job waited for.

答案 1 :(得分:25)

使用GNU Parallel将使您的脚本更短,可能更高效:

parallel 'echo "Processing "{}" ..."; do_something_important {}' ::: apache-*.log

这将为每个CPU核心运行一个作业,并继续执行此操作,直到处理完所有文件。

您的解决方案基本上会在运行前将作业拆分为多个组。这里有4组32个职位:

Simple scheduling

GNU Parallel会在完成后生成一个新进程 - 保持CPU处于活动状态,从而节省时间:

GNU Parallel scheduling

了解更多信息:

答案 2 :(得分:3)

我最近必须这样做并最终得到以下解决方案:

while true; do
  wait -n || {
    code="$?"
    ([[ $code = "127" ]] && exit 0 || exit "$code")
    break
  }
done;

以下是它的工作原理:

只要其中一个(可能很多)后台作业退出,

wait -n就会退出。它总是计算为true,循环继续,直到:

  1. 退出代码127:成功退出最后一个后台作业。在 在这种情况下,我们忽略退出代码并使用代码退出子shell 0
  2. 任何后台作业失败。我们只用退出代码退出子shell。
  3. 使用set -e,这将保证脚本提前终止并通过任何失败的后台作业的退出代码。

答案 3 :(得分:0)

这是我的原始解决方案:

function run_task {
        cmd=$1
        output=$2
        concurency=$3
        if [ -f ${output}.done ]; then
                # experiment already run
                echo "Command already run: $cmd. Found output $output"
                return
        fi
        count=`jobs -p | wc -l`
        echo "New active task #$count:  $cmd > $output"
        $cmd > $output && touch $output.done &
        stop=$(($count >= $concurency))
        while [ $stop -eq 1 ]; do
                echo "Waiting for $count worker threads..."
                sleep 1
                count=`jobs -p | wc -l`
                stop=$(($count > $concurency))
        done
}

这个想法是使用" jobs"看看有多少孩子在后台活动并等到这个数字下降(一个孩子退出)。一旦孩子存在,就可以开始下一个任务。

正如您所看到的,还有一些额外的逻辑可以避免多次运行相同的实验/命令。它为我完成了工作。但是,可以跳过或进一步改进此逻辑(例如,检查文件创建时间戳,输入参数等)。