Как ожидать в bash-скрипте нескольких подпроцессов, порожденных этим скриптом, чтобы завершить работу и вернуть код завершения! = 0, когда любой из подпроцессов заканчивается кодом! = 0?
Простой скрипт:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
Приведенный выше скрипт будет ожидать всех 10 порожденных подпроцессов, но он всегда будет иметь статус выхода 0 (см. help wait
). Как я могу изменить этот скрипт, чтобы он обнаруживал состояния выхода порожденных подпроцессов и возвращал код выхода 1, когда любой из подпроцессов заканчивался кодом! = 0?
Есть ли лучшее решение для этого, чем сбор PID подпроцессов, ожидание их в порядке и суммирование состояний выхода?
wait -n
, доступно в современном bash для возврата только после завершения первой / следующей команды.wait -n
есть одна небольшая проблема: если не осталось дочерних заданий (так называемое состояние гонки), он возвращает ненулевое состояние завершения (сбой), которое может быть неотличимо от сбойного дочернего процесса.Ответы:
wait
также (опционально) принимает PID ожидаемого процесса и с $! Вы получаете PID последней команды, запущенной в фоновом режиме. Измените цикл, чтобы сохранить PID каждого порожденного подпроцесса в массиве, а затем снова выполните цикл ожидания каждого PID.источник
for i in $n_procs; do ./procs[${i}] & ; pids[${i}]=$!; done; for pid in ${pids[*]}; do wait $pid; done;
, не так ли?http://jeremy.zawodny.com/blog/archives/010717.html :
источник
jobs -p
дает PID подпроцессов, которые находятся в состоянии выполнения. Процесс будет пропущен, если процесс завершится доjobs -p
вызова. Таким образом, если какой-либо из подпроцессов завершится раньшеjobs -p
, состояние выхода этого процесса будет потеряно.jobs -p
, не дает PID подпроцессов, а вместо этого GPID . Кажется, что логика ожидания работает в любом случае, она всегда ожидает группу, если такая группа существует, и pid, если нет, но это полезно знать ... особенно, если кто-то должен опираться на это и включать что-то вроде отправки сообщений в подпроцесс, в котором случай, когда синтаксис отличается в зависимости от того, есть ли у вас PID или GPID .. т.е.kill -- -$GPID
противkill $PID
Вот простой пример использования
wait
.Запустите несколько процессов:
Затем дождитесь их
wait
командой:Или просто
wait
(без аргументов) для всех.Это будет ожидать завершения всех заданий в фоновом режиме.
Если
-n
опция указана, ждет завершения следующего задания и возвращает статус завершения.Смотрите:
help wait
иhelp jobs
для синтаксиса.Однако недостатком является то, что это вернет только состояние последнего идентификатора, поэтому вам нужно проверить состояние каждого подпроцесса и сохранить его в переменной.
Или сделайте свою функцию вычисления, чтобы создать какой-либо файл при сбое (пустой или с ошибкой), а затем проверить этот файл, если он существует, например,
источник
sleep 20 && true
иsleep 20 && false
- а именно: заменить те , с функцией (ов). Чтобы понять&&
и||
, запуститеman bash
и введите «/» (поиск), затем «^ * Lists» (регулярное выражение), затем введите: man прокрутите вниз до описания&&
и||
||
как перехватить STDERR при сбое.wait
Если у вас установлен GNU Parallel, вы можете сделать:
GNU Parallel даст вам код выхода:
0 - все задания выполнены без ошибок.
1-253 - Не удалось выполнить некоторые задания. Статус выхода дает количество неудачных заданий
254 - более 253 рабочих мест не удалось.
255 - Другая ошибка.
Посмотрите вступительные видео, чтобы узнать больше: http://pi.dk/1
источник
doCalculations
есть функция, определенная в том же сценарии (хотя OP не прояснил это требование). Когда я пытаюсь,parallel
говорит/bin/bash: doCalculations: command not found
(он говорит это 10 раз дляseq 0 9
примера выше). Смотрите здесь для обхода.xargs
имеет возможность запускать задания параллельно с помощью-P
опции. От сюда :export -f doCalculations ; seq 0 9 |xargs -P 0 -n 1 -I{} bash -c "doCalculations {}"
. Ограниченияxargs
перечислены в справочной странице дляparallel
.doCalculations
используются какие-либо другие переменные среды сценария (пользовательскиеPATH
и т. Д.), Их, вероятно, необходимо явноexport
отредактировать перед запускомparallel
.wget -O - pi.dk/3 | sh
вы не получите путаницы. Если ваш упаковщик все испортил для вас, я призываю вас поднять проблему с вашим упаковщиком. Переменные и функции должны быть экспортированы (экспорт -f) для GNU Parallel, чтобы увидеть их (см .man parallel
: gnu.org/software/parallel/… )Как насчет просто:
Обновить:
Как указывалось несколькими комментаторами, приведенное выше ожидает завершения всех процессов перед продолжением, но не завершается и не завершается ошибкой в случае сбоя одного из них. Это можно сделать с помощью следующей модификации, предложенной @Bryan, @SamBrightman и другими. :
источник
for pid in $pids; do wait $pid; done
help wait
- с несколькими идентификаторамиwait
возвращает код выхода только последнего, как было сказано выше @ vlad-frolov.wait
будет вызван соответствующий процесс ? Оказывается, это не проблема: если вы находитесьwait
в процессе, который уже завершен,wait
немедленно выйдите из него со статусом уже завершившегося процесса. (Спасибо,bash
авторы!)Вот что я придумала до сих пор. Я хотел бы увидеть, как прервать команду сна, если ребенок завершает работу, чтобы не пришлось настраиваться
WAITALL_DELAY
на его использование.источник
Чтобы распараллелить это ...
Переведите это на это ...
--max-procs
на основе того, сколько параллелизма вы хотите (0
означает «все сразу»).xargs
- но он не всегда устанавливается по умолчанию.for
Цикл не является строго необходимым в этом примере , так какecho $i
в основном только регенерирующий выход$(whatever_list
). Я просто думаю, что использованиеfor
ключевого слова немного облегчает понимание того, что происходит.Вот упрощенный рабочий пример ...
источник
--max-procs
: Как получить количество процессоров / ядер в Linux из командной строки?Я не верю, что это возможно с помощью встроенной функциональности Bash.
Вы можете получить уведомление, когда ребенок выходит:
Однако нет очевидного способа получить статус выхода ребенка в обработчике сигнала.
Получение этого дочернего статуса обычно является задачей
wait
семейства функций в API POSIX нижнего уровня. К сожалению, поддержка Bash для этого ограничена - вы можете подождать одного конкретного дочернего процесса (и получить его статус завершения) или вы можете подождать всех их, и всегда получите 0 результата.То, что кажется невозможным сделать, - это эквивалент
waitpid(-1)
, который блокирует, пока какой-либо дочерний процесс не вернется.источник
Я вижу много хороших примеров, перечисленных здесь, хотел бы добавить и мой.
Я использую что-то очень похожее для параллельного запуска / остановки серверов / сервисов и проверки каждого состояния выхода. Прекрасно работает для меня. Надеюсь, это поможет кому-то!
источник
trap "kill 0" EXIT
Это то, что я использую:
источник
Следующий код будет ожидать завершения всех вычислений и вернет состояние выхода 1, если произойдет сбой любого из doCalculations .
источник
Просто сохраните результаты из оболочки, например, в файле.
источник
Вот моя версия, которая работает для нескольких pids, регистрирует предупреждения, если выполнение занимает слишком много времени, и останавливает подпроцессы, если выполнение занимает больше заданного значения.
Например, дождитесь завершения всех трех процессов, запишите предупреждение, если выполнение занимает менее 5 секунд, остановите все процессы, если выполнение займет более 120 секунд. Не выходите из программы при сбоях.
источник
Если у вас есть bash 4.2 или более поздняя версия, вам может быть полезно следующее. Он использует ассоциативные массивы для хранения имен задач и их «кода», а также имен задач и их pids. Я также встроил простой метод ограничения скорости, который может пригодиться, если ваши задачи потребляют много ресурсов ЦП или ввода-вывода и вы хотите ограничить количество одновременных задач.
Скрипт запускает все задачи в первом цикле и использует результаты во втором.
Это немного излишне для простых случаев, но допускает довольно аккуратные вещи. Например, можно хранить сообщения об ошибках для каждой задачи в другом ассоциативном массиве и распечатывать их после того, как все установится.
источник
Я только что изменил сценарий, чтобы фон и распараллелить процесс.
Я провел некоторые эксперименты (в Solaris с bash и ksh) и обнаружил, что «wait» выводит состояние выхода, если оно не равно нулю, или список заданий, которые возвращают ненулевой выход, если не указан аргумент PID. Например
Bash:
КШ:
Этот вывод записывается в stderr, поэтому простое решение для примера OP может быть:
Пока это:
также вернет счетчик, но без файла tmp. Это также может быть использовано следующим образом, например:
Но это не намного полезнее, чем IMO-файл tmp. Я не смог найти полезного способа избежать файла tmp, а также избежать запуска «wait» в подоболочке, которая вообще не будет работать.
источник
Я попробовал это и объединил все лучшие части из других примеров здесь. Этот скрипт будет выполнять
checkpids
функцию при выходе из любого фонового процесса и выводить состояние выхода, не прибегая к опросу.источник
set -m
позволяет использовать fg & bg в скриптеfg
в дополнение к тому, что последний процесс находится на переднем плане, имеет тот же статус выхода, что и процесс, который он на переднем планеwhile fg
прекратит зацикливание, когда любыеfg
выходы с ненулевым статусом выходак сожалению, это не будет обрабатывать случай, когда процесс в фоновом режиме завершает работу с ненулевым состоянием выхода. (Цикл не прекратится немедленно. Он будет ждать завершения предыдущих процессов.)
источник
Здесь уже есть много ответов, но я удивлен, что никто, похоже, не предложил использовать массивы ... Итак, вот что я сделал - это может быть полезно для некоторых в будущем.
источник
Это работает, должно быть так же хорошо, если не лучше, чем ответ @ HoverHell!
и, конечно, я увековечил этот сценарий в проекте NPM, который позволяет вам параллельно запускать команды bash, полезные для тестирования:
https://github.com/ORESoftware/generic-subshell
источник
trap $? $$
Кажется, каждый раз для меня устанавливается код выхода 0 и PID для текущей запущенной оболочки bashловушка твой друг. Вы можете поймать ERR во многих системах. Вы можете перехватить EXIT или DEBUG, чтобы выполнить фрагмент кода после каждой команды.
Это в дополнение ко всем стандартным сигналам.
источник
set -e
В верхней делает вашу остановку сценария на провал.expect
вернется,1
если какая-либо работа не удалась.источник
Именно для этого я написал
bash
функцию под названием:for
.Примечание :
:for
не только сохраняет и возвращает код завершения сбойной функции, но также завершает все параллельно работающие экземпляры. Который может не понадобиться в этом случае.Применение
for.sh
:Ссылки
источник
Я использовал это недавно (спасибо Alnitak):
Оттуда можно легко экстраполировать и иметь триггер (коснуться файла, отправить сигнал) и изменить критерии подсчета (количество файлов, к которым обращались или что-то еще), чтобы ответить на этот триггер. Или, если вы просто хотите «любой» ненулевой rc, просто убейте блокировку из save_status.
источник
Мне это нужно, но целевой процесс не был дочерним по отношению к текущей оболочке, в этом случае
wait $PID
не работает. Вместо этого я нашел следующую альтернативу:Это зависит от наличия procfs , который может быть недоступен (например, Mac не предоставляет его). Так что для переносимости вы можете использовать это вместо:
источник
Сигнал перехвата CHLD может не работать, потому что вы можете потерять некоторые сигналы, если они поступили одновременно.
источник
Решение для ожидания нескольких подпроцессов и выхода, когда любой из них завершается с ненулевым кодом состояния, заключается в использовании wait -n
Код состояния «127» предназначен для несуществующего процесса, что означает, что ребенок мог выйти.
источник
Дождитесь всех заданий и верните код завершения последнего невыполненного задания. В отличие от решений выше, это не требует сохранения pid. Просто подожди и подожди.
источник
Может быть случай, когда процесс завершен, прежде чем ждать его. Если мы инициируем ожидание процесса, который уже завершен, он вызовет ошибку, как pid не является дочерним элементом этой оболочки. Чтобы избежать таких случаев, можно использовать следующую функцию, чтобы определить, завершен ли процесс или нет:
источник
Я думаю, что самый простой способ параллельно выполнять задания и проверять их состояние - использовать временные файлы. Уже есть пара похожих ответов (например, Nietzche-jou и mug896).
Приведенный выше код не является потокобезопасным. Если вы обеспокоены тем, что приведенный выше код будет работать одновременно с самим собой, лучше использовать более уникальное имя файла, например fail. $$. Последняя строка должна выполнить требование: «вернуть код выхода 1, когда любой из подпроцессов заканчивается кодом! = 0?» Я бросил дополнительное требование, чтобы убрать. Возможно, было бы яснее написать это так:
Вот аналогичный фрагмент для сбора результатов из нескольких заданий: я создаю временный каталог, записываю выходные данные всех подзадач в отдельный файл и затем выгружаю их для просмотра. Это не совсем соответствует вопросу - я добавляю это в качестве бонуса:
источник
Я чуть не попал в ловушку использования
jobs -p
PID для сбора PID, что не работает, если ребенок уже вышел, как показано в приведенном ниже сценарии. Решением, которое я выбрал, было просто вызватьwait -n
N раз, где N - это число моих детей, которое я знаю детерминистически.Вывод:
источник