Дождитесь окончания процесса

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

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

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

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

Ответ 1

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

Linux:

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

Darwin (требуется, чтобы $pid имел открытые файлы):

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

С таймаутом (в секундах)

Linux:

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

Darwin (требуется, чтобы $pid имел открытые файлы):

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)

Ответ 2

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

anywait(){

    for pid in "[email protected]"; do
        while kill -0 "$pid"; do
            sleep 0.5
        done
    done
}

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

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

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

Ответ 3

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

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

У этого был бы недостаток, вероятно, совпадающий с зомби-процессами.

Ответ 4

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

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

EDIT: приведенный выше script был приведен ниже Rockallite. Спасибо!

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

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

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

Ответ 5

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

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

Ответ 6

Из bash manpage

   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  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.

Ответ 7

Хорошо, так что кажется, что ответ - нет, встроенного инструмента нет.

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

strace -qqe '' -p <PID>

Ответ 8

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

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

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

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

Решение 2 (используя верхнюю команду и имя процесса):

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 (с использованием верхней команды и идентификатора процесса):

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

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

Ответ 9

Я нашел небольшую утилиту, которая ждет на любом PID и сразу же возвращается, когда 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);
}

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

Ответ 10

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

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

Ответ 11

Имел ту же проблему, я решил проблему, убив процесс, а затем ожидая завершения каждого процесса с использованием файловой системы PROC:

while [ -e /proc/${pid} ]; do sleep 0.1; done

Ответ 12

В такой системе, как OSX, у вас может не быть pgrep, поэтому вы можете попробовать этот метод оценки при поиске процессов по имени:

while ps axg | grep process_name$ > /dev/null; do sleep 1; done

Символ $ в конце имени процесса гарантирует, что grep сопоставляет только имя_процесса с концом строки в выводе ps, а не сам.

Ответ 13

Теперь вы можете использовать wait -n bash для этой цели.

В руководстве Bash говорится:

wait [-n] [n...]

<...> Если включена опция -n, ожидание ожидает завершения любого задания и возврата его статуса выхода. <...>

Ответ 14

Решение для блокировки

Используйте wait в цикле, ожидая завершения всех процессов:

function anywait()
{

    for pid in "[email protected]"
    do
        wait $pid
        echo "Process $pid terminated"
    done
    echo 'All processes terminated'
}

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

Неблокирующее решение

Используйте kill -0 в цикле, ожидая завершения всех процессов + сделать что-нибудь между проверками:

function anywait_w_status()
{
    for pid in "[email protected]"
    do
        while kill -0 "$pid"
        do
            echo "Process $pid still running..."
            sleep 1
        done
    done
    echo 'All processes terminated'
}

Время реакции уменьшилось до sleep, так как должно предотвращать высокую загрузку процессора.

Реалистичное использование:

Ожидание завершения всех процессов + информирование пользователя о all запуске PID.

function anywait_w_status2()
{
    while true
    do
        alive_pids=()
        for pid in "[email protected]"
        do
            kill -0 "$pid" 2>/dev/null \
                && alive_pids+="$pid "
        done

        if [ ${#alive_pids[@]} -eq 0 ]
        then
            break
        fi

        echo "Process(es) still running... ${alive_pids[@]}"
        sleep 1
    done
    echo 'All processes terminated'
}

Примечания

Эти функции получают PID через аргументы [email protected] как массив BASH.