У меня есть команда CMD, вызываемая из моего основного сценария оболочки bourne, которая занимает вечность.
Я хочу изменить сценарий следующим образом:
- Запустите команду CMD параллельно в фоновом режиме (
CMD &
). - В основном сценарии создайте цикл для отслеживания порожденной команды каждые несколько секунд. Цикл также выводит на стандартный вывод некоторые сообщения, указывающие на ход выполнения скрипта.
- Выйдите из цикла, когда порожденная команда завершится.
- Захватить и сообщить код выхода порожденного процесса.
Может кто-нибудь подскажет, как это сделать?
Ответы:
1: В bash
$!
содержит PID последнего выполненного фонового процесса. Это все равно скажет вам, какой процесс отслеживать.4:
wait <n>
ожидает завершения процесса с PID<n>
(он будет блокироваться, пока процесс не завершится, поэтому вы, возможно, не захотите вызывать это, пока не будете уверены, что процесс завершен), а затем возвращает код выхода завершенного процесса.2, 3:
ps
илиps | grep " $! "
может сказать вам, выполняется ли процесс. Вам решать, как понять результат и решить, насколько он близок к завершению. (ps | grep
не является защитой от идиотов. Если у вас есть время, вы можете придумать более надежный способ узнать, выполняется ли процесс еще).Вот скелетный сценарий:
источник
ps -p $my_pid -o pid=
ни то, ни другоеgrep
не требуется.kill -0 $!
- лучший способ узнать, выполняется ли процесс. На самом деле он не отправляет никакого сигнала, а только проверяет, что процесс жив, используя встроенную оболочку вместо внешних процессов. Какman 2 kill
сказано: «Если sig равен 0, то сигнал не отправляется, но проверка ошибок по-прежнему выполняется; это можно использовать для проверки наличия идентификатора процесса или идентификатора группы процессов».kill -0
вернет ненулевое значение, если у вас нет разрешения на отправку сигналов запущенному процессу. К сожалению, он возвращается1
как в этом случае, так и в случае, когда процесс не существует. Это делает его полезным, если вы не являетесь владельцем процесса - что может иметь место даже для процессов, которые вы создали, еслиsudo
задействован такой инструмент, или если они установлены с установленным идентификатором (и, возможно, отбрасывают привилегии).wait
не возвращает код выхода в переменной$?
. Он просто возвращает код выхода и$?
является кодом выхода последней программы переднего плана.kill -0
. Вот рецензируемая ссылка от SO, которая показывает, что комментарий CraigRinger является законным в отношении:kill -0
будет возвращать ненулевое значение для запущенных процессов ... ноps -p
всегда будет возвращать 0 для любого запущенного процесса .Вот как я решил это, когда у меня была аналогичная потребность:
источник
wait
s заставляют сценарий ждать до самого конца (каждого) процесса.Pid фонового дочернего процесса хранится в $! , Вы можете хранить идентификаторы всех дочерних процессов в массиве, например, PIDS [] .
Подождите, пока дочерний процесс, указанный в каждом идентификаторе процесса или спецификации задания, не завершится, и не вернет статус выхода последней ожидаемой команды. Если задана спецификация задания, ожидаются все процессы в задании. Если аргументы не указаны, ожидаются все активные в данный момент дочерние процессы, и статус возврата равен нулю. Если указана опция -n, wait ожидает завершения любого задания и возвращает его статус завершения. Если ни jobpec, ни pid не указывают активный дочерний процесс оболочки, статус возврата - 127.
Используйте команду wait, вы можете дождаться завершения всех дочерних процессов, в то время как вы можете получить статус выхода каждого дочернего процесса с помощью $? и сохранить статус в СТАТУС [] . Тогда вы можете делать что-то в зависимости от статуса.
Я пробовал следующие 2 решения, и они работают хорошо. solution01 более лаконичен, а solution02 немного сложнее.
solution01
solution02
источник
pid=$!; PIDS[$i]=${pid}; ((i+=1))
можно написать более просто, какPIDS+=($!)
который просто добавляется к массиву без необходимости использования отдельной переменной для индексации или самого pid. То же самое и сSTATUS
массивом.Как я вижу, почти во всех ответах используются внешние утилиты (в основном
ps
) для опроса состояния фонового процесса. Есть более unixesh-решение, перехватывающее сигнал SIGCHLD. В обработчике сигналов необходимо проверить, какой дочерний процесс был остановлен. Это можно сделать с помощьюkill -0 <PID>
встроенного (универсального) или проверки существования/proc/<PID>
каталога (для Linux) или с помощьюjobs
встроенного (ударконкретный.jobs -l
также сообщает pid. В этом случае 3-е поле вывода может быть Остановлено | Выполнено | Готово | Выход. ).Вот мой пример.
Запущенный процесс называется
loop.sh
. Он принимает-x
в качестве аргумента число или. Для-x
выхода с кодом выхода 1. Для числа он ждет num * 5 секунд. Каждые 5 секунд он печатает свой PID.Процесс запуска называется
launch.sh
:Дополнительные сведения см. В разделе: Не удалось запустить процесс из сценария bash.
источник
for i in ${!pids[@]};
с использованием расширения параметров.источник
grep -v
. Вы можете ограничить поиск до начала строки:grep '^'$pid
Плюс, вы всеps p $pid -o pid=
равно можете это сделать . Кроме того,tail -f
он не закончится, пока вы его не убьете, поэтому я не думаю, что это очень хороший способ продемонстрировать это (по крайней мере, не указывая на это). Возможно, вы захотите перенаправить вывод своейps
команды,/dev/null
иначе он будет отображаться на экране при каждой итерации. Вашиexit
причиныwait
пропуска файла - вероятно, это должен быть файлbreak
. Но развеwhile
/ps
и неwait
лишние?kill -0 $pid
? На самом деле он не отправляет никакого сигнала, а только проверяет, что процесс жив, используя встроенную оболочку вместо внешних процессов.bash: kill: (1) - Operation not permitted
Я бы немного изменил ваш подход. Вместо того, чтобы проверять каждые несколько секунд, активна ли команда и сообщать о сообщении, используйте другой процесс, который каждые несколько секунд сообщает, что команда все еще выполняется, а затем завершает этот процесс, когда команда завершается. Например:
источник
while kill -0 $pid 2> /dev/null; do X; done
, надеюсь, это будет полезно для кого-то в будущем, кто прочитает это сообщение;)У нашей команды была такая же потребность в удаленном SSH-скрипте, который отключался после 25 минут бездействия. Вот решение, в котором цикл мониторинга проверяет фоновый процесс каждую секунду, но печатает только каждые 10 минут, чтобы подавить тайм-аут бездействия.
источник
Простой пример, аналогичный приведенным выше решениям. Это не требует мониторинга выходных данных процесса. В следующем примере хвост используется для отслеживания вывода.
Используйте tail, чтобы следить за выходными данными процесса и выйти, когда процесс будет завершен.
источник
Другое решение - контролировать процессы через файловую систему proc (безопаснее, чем комбинация ps / grep); когда вы запускаете процесс, у него есть соответствующая папка в / proc / $ pid, поэтому решение может быть
Теперь вы можете использовать переменную $ exit_status как хотите.
источник
Syntax error: "else" unexpected (expecting "done")
С помощью этого метода ваш сценарий не должен ждать фонового процесса, вам нужно будет только отслеживать временный файл для статуса выхода.
теперь ваш скрипт может делать что угодно, а вам просто нужно следить за содержимым retFile (он также может содержать любую другую информацию, которую вы хотите, например время выхода).
PS: кстати, я закодировал мышление на bash
источник
Это может выходить за рамки вашего вопроса, однако, если вас беспокоит продолжительность выполнения процессов, вам может быть интересно проверить статус запущенных фоновых процессов через определенный промежуток времени. Достаточно легко проверить, какие дочерние PID все еще используются
pgrep -P $$
, однако я придумал следующее решение, чтобы проверить статус выхода тех PID, срок действия которых уже истек:который выводит:
Примечание: вы можете изменить
$pids
строковую переменную, а не массив, чтобы упростить задачу, если хотите.источник
Мое решение состояло в том, чтобы использовать анонимный канал для передачи статуса в цикл мониторинга. Временные файлы, используемые для обмена статусом, не используются, поэтому очищать нечего. Если вы не уверены в количестве фоновых заданий, причиной может быть остановка
[ -z "$(jobs -p)" ]
.источник