Подтвердить что ты не робот

Подождите, пока будут выполнены фоновые задания bash в script

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

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

Я вполне доволен этим рабочим решением, однако я не мог понять, как писать дополнительный код, который выполняется только после завершения всех циклов.

Есть ли способ получить контроль над этим?

4b9b3361

Ответ 1

Для этого существует встроенная команда 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.

Ответ 2

Использование GNU Parallel сделает ваш script еще короче и, возможно, более эффективным:

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

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

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

Simple scheduling

GNU Parallel вместо этого запускает новый процесс, когда заканчивается - сохранение активных процессоров и, следовательно, экономия времени:

GNU Parallel scheduling

Чтобы узнать больше:

  • Смотрите видеоролик для быстрого введения: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
  • Пройдите через учебник (man parallel_tutorial). Вы в командной строке будет любить вас за это.

Ответ 3

Мне нужно было сделать это недавно, и в итоге получилось следующее решение:

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

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

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

  • Код выхода 127: последнее фоновое задание успешно завершено. В в этом случае мы игнорируем код выхода и выходим из под-оболочки с кодом 0.
  • Не удалось выполнить какое-либо фоновое задание. Мы просто выходим из под-оболочки с этим кодом выхода.

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

Ответ 4

Это мое грубое решение:

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
}

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

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