ПОДОЖДИТЕ, чтобы «любой процесс» закончился

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

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

<code>while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
</code>
 teika kazura15 июн. 2013 г., 09:41
Позвольте мне датьtwo cautions: 1. Какpointed out below отmp3foley, & quot; убить -0 & quot; не всегда работает для POSIX. 2. Вероятно, вы также хотите убедиться, что процесс не является зомби, который является практически завершенным процессом.See mp3foley's comment а такжеmine для деталей.
 Marco Marsala18 июн. 2014 г., 16:19
отличная идея! Спасибо
 teika kazura09 мая 2015 г., 08:23
Another caution (originally pointed out отks1322 ниже): Использование PID, отличного от дочернего процесса, не является надежным. Если вы хотите безопасный способ, используйте, например, IPC.

Ответы на вопрос(13)

To wait for any process to finish

Linux:

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

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

lsof -p $pid +r 1 &>/dev/null
With timeout (seconds)

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)
 28 июн. 2018 г., 13:57
Этот трюкfails для зомби. Это нормально для процессов, которые вы не можете убить;tail имеет линиюkill (pid, 0) != 0 && errno != EPERM.
 30 июл. 2017 г., 17:06
Кто бы знал, чтоtail сделал бы это.
 21 нояб. 2017 г., 02:47
tail работает под капотом путем опросаkill(pid, SIG_0) к процессу (обнаружен с помощьюstrace).
 28 февр. 2018 г., 17:58
Обратите внимание, что lsof использует опрос, что+r 1 Это тайм-аут, я лично ищу решение для MacOS, которое не использует опрос.
 18 авг. 2017 г., 01:34
Похоже, что решение для Linux работает на Cygwin и Solaris, основываясь на кратком тесте сsleep (Хотяpwait решение ниже для соляриса проще)

kill -0 в цикле для работоспособного решения:

anywait(){

    for pid in "[email protected]"; 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 вызов. В Linuxtest -d "/proc/$pid" работает, в других системах вам, возможно, придется использоватьpgrep (если есть) или что-то вродеps | grep ^$pid.

 30 сент. 2014 г., 18:37
@ ks1322 Да, в этом коде действительно есть условие гонки.
 06 мая 2014 г., 08:58
Caution 2 (О зомби). Последующего комментария от Тедди пока недостаточно, поскольку они могут быть зомби. Увидетьmy answer below для решения Linux.
 28 апр. 2016 г., 23:57
PID обычно не генерируются последовательно? Какова вероятность того, что отсчет оборачивается за одну секунду?
 15 июн. 2013 г., 09:39
Caution: Это не всегда работает, так какpointed out below отmp3foley, Смотрите этот комментарий иmine для деталей.
 26 сент. 2014 г., 15:52
Не рискует ли это решение состоянием гонки? Во время снаsleep 0.5обработать с$pid может умереть, и другой процесс может быть создан с тем же$pid, И мы в конечном итоге ждем 2 разных процесса (или даже больше) с тем же$pid.

   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.
 13 дек. 2012 г., 14:50
+1 заchild of the current shell.
 30 авг. 2016 г., 10:20
Для возврата @coderofsalvation (сон 10 и сон 3 и ожидание) требуется 10 секунд: ожидание без аргументов заблокирует, пока ВСЕ дочерние процессы не завершатся. ОП хочет получить уведомление о завершении первого дочернего (или назначенного) процесса.
 18 авг. 2017 г., 01:21
Также не работает, если процесс не основан или не основан (на Solaris, Linux или Cygwin). ех.sleep 1000 ctrl-z wait [sleep pid] немедленно возвращается
 04 апр. 2016 г., 09:54
@gumik:"If n is not given, all currently active child processes are waited for" , Это прекрасно работает ..wait без аргументов заблокирует процесс доany child процессы заканчиваются. Честно говоря, я не вижу смысла ждатьany процесс, так как всегда происходят постоянные процессы.
 25 февр. 2012 г., 10:47
Это правда, но он может только ждать дочернего элемента текущей оболочки. Вы не можете ждатьany процесс.
Blocking solution

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

function anywait()
{

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

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

Non-blocking solution

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 время, потому что должны предотвратить высокую загрузку процессора.

A realistic usage:

Ожидание завершения всех процессов + информирование пользователя о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'
}
Notes

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

если процесс не существует или он является зомби.

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

EDIT: Приведенный выше скриптбыл дан ниже  отRockallite, Спасибо!

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

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

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

 07 сент. 2016 г., 03:48
Или жеwhile s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do sleep 1; done
 08 мая 2014 г., 09:25
Хм ... просто попробовал еще раз, и это сработало. Я думаю, я сделал что-то не так в прошлый раз.
 25 февр. 2014 г., 08:15
Хороший. Хотя я должен был окружитьgrep /proc/$PID/status с двойными кавычками (bash: test: argument expected)

которая ждет любого 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);
}

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

 07 мар. 2018 г., 22:51
зачем использоватьwaitid() вместоpwait()?
 24 мая 2017 г., 15:28
Вот Это Да! Это так круто! Спасибо! И спасибо автору! Просто не забудьтеsetcap cap_sys_ptrace+ep waiter (см. Makefile в git-репо)
 28 февр. 2018 г., 18:04
Я предполагаю, что это будет работать, только если pid представляет дочерний элемент текущего процесса?
 03 янв. 2017 г., 20:22
В самом деле,ptrace(2) кажется кроссплатформенным решением, но ваша маленькая программа требует немного больше работы. Прежде всего,PTRACE_SEIZE не определено ни для BSD (конечно, не для FreeBSD-10.x), ни для более старых Linux (не для CentOS-6.8) - этоa recent addition (ядро 3.4).PT_ATTACH следует использовать вместо Что еще более важно, вызов может вернуться, когда субъектный процесс получает сигнал. Чтобы избежать преждевременного выхода, утилита должна быть в состоянии обнаружить это и вернуться вptrace...

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

Solution 1 (by using ps command): Just to add up to Pierz answer, I would suggest:

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.

Solution 2 (by using top command and process name):

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

Solution 3 (by using top command and process ID):

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

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

 01 июл. 2016 г., 11:33
Downvote: длинныйgrep -v grep pipe - это массивный антипаттерн, и это предполагает, что у вас нет связанных процессов с тем же именем. Если вы знаете PID вместо этого, это может быть адаптировано к правильно работающему решению.
 03 июл. 2016 г., 23:29
Спасибо tripleee за комментарий. Я добавил флаг-w чтобы избежать проблемыgrep -v grep to some extent, Я также добавил еще два решения на основе вашего комментария.

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

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

 17 авг. 2017 г., 03:24
plink [email protected] box -pw redacted "pwait 6998";email -b -s "It's done" etc просто позволил мне идти домой, а не через несколько часов.
 17 июн. 2017 г., 05:24
Я хотел бы, чтобы я мог ответить на этот вопрос десятки раз.
 03 янв. 2017 г., 20:25
Кажется, ты прав. Меа виноват ... Все они реализуютkqueueтак что компилируем FreeBSDpwait(1) было бы тривиально, однако. Почему бы другой функции импорта BSD не ускользнуло от меня ...
 02 янв. 2017 г., 23:39
> BSD and Solaris: Проверка трех больших BSD, которые приходят на ум; ни OpenBSD, ни NetBSD не имеют этой функции (на их man-страницах), только FreeBSD, как вы можете легко проверитьman.openbsd.org.

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

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

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

 14 июн. 2013 г., 14:00
Хорошее наблюдение. В POSIX системный вызовkill(pid, sig=0) завершается неудачей, если вызывающий процесс не имеет привилегии для уничтожения. Таким образом, / bin / kill -0 и & quot; kill -0 & quot; (встроенный bash) тоже не работает при тех же условиях.

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

while [ -e /proc/${pid} ]; do sleep 0.1; done
 28 февр. 2018 г., 09:26
Голосование очень плохо, вы могли бы получить в гости от полиции :)

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

strace -qqe '' -p <PID>
 19 авг. 2014 г., 18:51
Хороший! Кажется, однако, что это не возможно, чтобы прикрепить к данному PID из двух разных мест (я получаюOperation not permitted для второго экземпляра); Вы можете это подтвердить?
 30 авг. 2014 г., 23:49
@eudoxos Да, на странице ptrace написано:(...)"tracee" always means "(one) thread" (и я подтверждаю ошибку, которую вы упоминаете). Чтобы позволить таким процессам ждать больше, вам нужно создать цепочку.

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

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

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

 15 авг. 2018 г., 11:23
Я не уверен, почему вы выбрали этот ответ как "Ужасный". - как подобный подход был предложен другими? Пока-q Предложение действительно, как я уже упоминал в своем ответе, в частности,$ означает, что grep не будет соответствовать имени "где-нибудь в командной строке" и при этом это не будет соответствовать. Вы действительно пробовали это на OSX?
 14 авг. 2018 г., 21:09
Ужасно: может быть несколько процессов с таким именемsomewhere in the command line, ваш собственный grep включен. Вместо перенаправления на/dev/null, -q следует использовать сgrep, Другой экземпляр процесса мог начаться, когда ваш цикл спал, и вы никогда не узнаете ...

Ваш ответ на вопрос