Дождитесь завершения фоновых заданий bash в скрипте

Чтобы максимально использовать процессор (я запускаю вещи на Debian Lenny в EC2), у меня есть простой скрипт для параллельного запуска заданий:

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

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

Вот как это работает:

wait -n завершается, как только завершается одно из (потенциально многих) фоновых заданий. Он всегда принимает значение true и цикл продолжается до тех пор, пока:

Exit code 127: the last background job successfully exited. In that case, we ignore the exit code and exit the sub-shell with code 0. Any of the background job failed. We just exit the sub-shell with that exit code.

Сset -eэто будет гарантировать, что сценарий завершится досрочно и пройдет через код завершения любого неудачного фонового задания.

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
}

Идея состоит в том, чтобы использовать «задания» чтобы увидеть, сколько детей активно в фоновом режиме, и подождите, пока это число не упадет (ребенок выходит). Когда ребенок существует, можно запустить следующую задачу.

Как видите, здесь также есть немного дополнительной логики, чтобы избежать многократного выполнения одних и тех же экспериментов / команд. Это делает работу за меня. Однако эту логику можно было либо пропустить, либо улучшить (например, проверить метки времени создания файла, входные параметры и т. Д.).

возможно, более эффективным:

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

Это запустит одно задание на ядро ЦП и будет продолжать это делать, пока все файлы не будут обработаны.

Ваше решение в основном разделит задания на группы перед запуском. Здесь 32 рабочих места в 4 группах:

Simple scheduling

GNU Parallel вместо этого порождает новый процесс после его завершения - поддерживая процессоры активными и, таким образом, экономя время:

GNU Parallel scheduling

Узнать больше:

Watch the intro video for a quick introduction: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 Walk through the tutorial (man parallel_tutorial). You command line will love you for it.
 05 окт. 2018 г., 23:35
Спасибо за параллель!
Решение Вопроса

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.
 04 мая 2017 г., 08:25
@lambacck не являетсяwait без аргумента эквивалентно?
 06 мая 2018 г., 00:33
Или использоватьwait $(jobs -rp) если у вас есть другие задания (например, когда вы приостановили vim с помощью Ctrl + Z): дополнительные-r флаг отфильтровываетrunning рабочие места.
 20 окт. 2016 г., 01:11
спасибо @lambacck
 mark15 июл. 2009 г., 16:03
Это было быстро и решило мою проблему, хорошо заработало, спасибо!
 27 апр. 2016 г., 16:31
подсказкаwait $(jobs -p) ждать вновь созданных рабочих мест.

Ваш ответ на вопрос