Bash threading: ждать завершения всех заданий, не работает?

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

Проблема, с которой я столкнулась, находится в самом конце script. То есть, основной поток не дожидался завершения всех потоков загрузки до выхода. Посмотрите на следующий упрощенный script (я удалил/изменил части кода, не связанные с проблемой)

function func {
for files in /home/somewhere/
  do
    echo "Uploading $1" &
  done
wait
}

find /home/some/path -type f | while read filename ; do
  echo "Creating archive of $filename"
  func $somevariable &
done

wait

Все выполняется очень красиво до тех пор, пока не будет создан последний архив, а затем script заканчивается перед тем, как закончится нить func, оставив много файлов не загруженным.

Спасибо за ваши идеи.

Ответ 1

Обновление: хорошие комментарии в комментарии.

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

Итак, сделайте что-нибудь вроде этого:

find /home/some/path -type f | (while read filename; do
    echo "Creating archive of $filename"
    func $somevariable &
  done
  wait
)

Ответ 2

Tricky! Проблема в том, что этот блок

find /home/some/path -type f | while read filename ; do
  ...
done

Создает подоболочку. В этой подоболочке создаются рабочие переменные func $. Родительская оболочка видит, что все фоновые задания, которые она создала, закончились, она не отслеживает фоновые задания, созданные подсветами, которые она породила.

Самое простое исправление - создать вместо этого исходные задания из родительской оболочки. Вы можете избежать создания подоболочки, не используя трубку:

while read filename ; do
  ...
done < <(find /home/some/path -type f)

Ну, это создает подоболочку --- для поиска --- но блок while больше не находится в подоболочке.

Обратите внимание, что вышеизложенное работает только под bash. (Не знаю о ksh или zsh, возможно, он работает и там, но он не будет работать под золой и другими производными.)

Ответ 3

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

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

Я подозреваю, что wait фактически ожидает только процессов/конвейеров, которые отображаются на выходе jobs. Попробуйте несколько экспериментов...

Возможной альтернативой может быть захват идентификаторов дочерних процессов и вызов wait n для каждого идентификатора.

Ответ 5

Вы можете зацикливаться до тех пор, пока команда jobs ничего не возвращает в качестве альтернативного метода.