Дождитесь окончания процесса - PullRequest
121 голосов
/ 29 июня 2009

Есть ли в Bash встроенная функция для ожидания завершения процесса?

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

Механический способ сделать это следующим образом, но я хотел бы знать, есть ли какая-либо встроенная функция в Bash.

while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done

Ответы [ 13 ]

89 голосов
/ 12 января 2017

Чтобы дождаться завершения любого процесса

Linux:

tail --pid=$pid -f /dev/null

Дарвин (требует, чтобы $pid имел открытые файлы):

lsof -p $pid +r 1 &>/dev/null

Со временем ожидания (в секундах)

Linux:

timeout $timeout tail --pid=$pid -f /dev/null

Дарвин (требует, чтобы $pid имел открытые файлы):

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)
77 голосов
/ 15 июля 2009

Там нет встроенного. Используйте kill -0 в цикле для работоспособного решения:

anywait(){

    for pid in "$@"; do
        while kill -0 "$pid"; do
            sleep 0.5
        done
    done
}

Или как простой способ для однократного использования:

while kill -0 PIDS 2> /dev/null; do sleep 1; done;

Как отметили несколько комментаторов, если вы хотите дождаться процессов, на которые у вас нет привилегии отправлять сигналы, вы найдете другой способ определить, работает ли процесс, чтобы заменить вызов kill -0 $pid. В Linux test -d "/proc/$pid" работает, в других системах вам, возможно, придется использовать pgrep (если доступно) или что-то вроде ps | grep "^$pid ".

50 голосов
/ 02 мая 2012

Я обнаружил, что "kill -0" не работает, если процесс принадлежит пользователю root (или другому), поэтому я использовал pgrep и придумал:

while pgrep -u root process_name > /dev/null; do sleep 1; done

Недостатком может быть, вероятно, совпадение процессов зомби.

31 голосов
/ 30 июля 2012

Этот цикл bash-скрипта заканчивается, если процесс не существует или это зомби.

PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do
    sleep 1
done

РЕДАКТИРОВАТЬ : вышеуказанный сценарий был дан ниже от Rockallite . Спасибо!

Мой оригинальный ответ ниже работает для Linux, полагаясь на procfs, т.е. /proc/. Я не знаю его мобильность:

while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
    sleep 1
done

Это не ограничивается оболочкой, но сами ОС не имеют системных вызовов для наблюдения за завершением не дочерних процессов.

12 голосов
/ 21 января 2015

FreeBSD и Solaris имеют эту удобную утилиту pwait(1), которая делает именно то, что вы хотите.

Я полагаю, что другие современные ОС также имеют необходимые системные вызовы (например, MacOS реализует BSD kqueue), но не все делают это доступным из командной строки.

9 голосов
/ 27 августа 2011

Из справочной страницы bash

   wait [n ...]
          Wait for each specified process and return its termination  status
          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 processes
          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.
5 голосов
/ 23 апреля 2015

Все эти решения протестированы в Ubuntu 14.04:

Решение 1 (с помощью команды ps): Просто, чтобы добавить к ответу Пирса, я бы предложил:

while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done

В этом случае grep -vw grep гарантирует, что grep соответствует только имя_процесса, а не самому grep. Преимущество заключается в поддержке случаев, когда имя_процесса находится не в конце строки в ps axg.

Решение 2 (с помощью команды top и имени процесса):

while [[ $(awk '$12=="process_name" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

Заменить process_name именем процесса, которое отображается в top -n 1 -b. Пожалуйста, держите кавычки.

Чтобы увидеть список процессов, которые вы ожидаете, чтобы завершить их, вы можете запустить:

while : ; do p=$(awk '$12=="process_name" {print $0}' <(top -n 1 -b)); [[ $b ]] || break; echo $p; sleep 1; done

Решение 3 (с помощью команды top и идентификатора процесса):

while [[ $(awk '$1=="process_id" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

Замените process_id на идентификатор процесса вашей программы.

5 голосов
/ 06 апреля 2014

Хорошо, похоже, ответ - нет, встроенного инструмента нет.

После установки /proc/sys/kernel/yama/ptrace_scope на 0 можно использовать программу strace. Другие переключатели могут быть использованы, чтобы заставить его замолчать, чтобы он действительно пассивно ожидал:

strace -qqe '' -p <PID>
3 голосов
/ 30 августа 2016

Я обнаружил небольшую утилиту, которая ждет любого PID и возвращает сразу же после его смерти.

https://github.com/izabera/waiter

Это крошечный C-файл, который вы можете скомпилировать с помощью gcc.

#define _GNU_SOURCE
#include <sys/ptrace.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
  if (argc == 1) return 1;
  pid_t pid = atoi(argv[1]);
  if (ptrace(PTRACE_SEIZE, pid, NULL, NULL) == -1) return 1;
  siginfo_t sig;
  return waitid(P_PID, pid, &sig, WEXITED|WNOWAIT);
}

Он работает так же, как отладчик при подключении к процессу.

2 голосов
/ 29 июня 2009

Нет встроенной функции для ожидания завершения какого-либо процесса.

Вы можете отправить kill -0 на любой найденный PID, так что вы не будете озадачены зомби и другими вещами, которые все еще будут видны в ps (пока все еще извлекаете список PID с помощью ps).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...