Дождитесь завершения фоновых заданий bash в скрипте - PullRequest
47 голосов
/ 15 июля 2009

Для максимального использования процессора (я запускаю вещи на 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 ]

76 голосов
/ 15 июля 2009

Для этого есть встроенная команда 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.
25 голосов
/ 28 марта 2014

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

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 голосов
/ 04 мая 2017

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

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

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

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

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

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

0 голосов
/ 18 мая 2015

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

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
}

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

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

...