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

StackOverflow https://stackoverflow.com/questions/1131484

  •  16-09-2019
  •  | 
  •  

Вопрос

Чтобы максимально использовать процессор (я запускаю все на 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 &
...

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

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

Это было полезно?

Решение

Есть 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.

Другие советы

Использование 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).Ваша командная строка полюбит вас за это.

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

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

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

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

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

С 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
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top