Дождитесь окончания процесса
Есть ли в 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.