Цикл до завершения фонового процесса (с `jobs`) не работает в скрипте

Цикл до завершения фонового процесса (с `jobs`) не работает в скрипте

Я пытаюсь зациклить, пока фоновый процесс (запущенный ранее в скрипте) не завершится. Легко воспроизводимый тестовый случай:

ping -c 10 localhost &>/dev/null &

В командной строке я могу выполнить цикл while [[ -n $(jobs) ]](пока $(jobs)не null).

$ ping -c 10 localhost &>/dev/null &
[1] 19078
$ while [[ -n $(jobs) ]]; do echo -n .; sleep 1; done
.........[1]+  Done                    ping -c 5 localhost &> /dev/null

Однако те же две строки в скрипте будут продолжать печатать .s до тех пор, пока я не нажму Ctrl-C.

Странно, но если я вызываю jobsвнутри цикла, скрипт завершается, как и ожидалось.

$ ./background-ping.sh
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Done                    ping -c 5 localhost &> /dev/null

Я знаю, что существуют и другие способы проверить, завершено ли фоновое задание (например, проверка /proc), но я хочу знать, почему проверка jobsне работает так, как ожидалось.

решение1

Если я правильно понял вашу проблему, ваши 2 строки не остановятся в сценарии.

Вот что я сделал в своем:

ping -c 5 google.com &>/dev/null &
while [[ -n $(jobs -r) ]]; do echo -n "."; sleep 1; done

jobs -rпроверяет запущенные процессы, и вызов моего скрипта работает как и ожидалось, скрипт останавливается после завершения ping.

EDIT: Я думаю, что в скрипте родительский процесс известен как запущенный процесс, и поэтому jobsпродолжайте думать, что есть запущенный процесс. Это гипотеза

Это ответ? (или, может быть, я действительно не понимаю, что вы имеете в виду, проблема может быть в моем плохом английском...)

решение2

Всякий раз, когда у вас возникают такие проблемы, вы всегда должны пытаться распечатать свои переменные, чтобы понимать, что происходит. В этом случае jobsтакже возвращает завершенные задания. Если вы запустите это:

ping -c 5 localhost &>/dev/null &
while [[ -n $(jobs | tee -a temp) ]]; do
    echo -n .; 
    sleep 1; 
done

Вы увидите следующий вывод temp:

[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[...]

Итак, вывод jobsздесь никогда не бывает пустым. Даже когда работа завершена, jobsвсе равно возвращает Doneсообщение. Вот почему решение Metal3d с использованием jobs -rсработало.

Что еще более запутанно, так это то, почему запуск jobsвнутри цикла заставляет его работать правильно. Ответ будет как-то связан с тем фактом, что он while [[ -n $(jobs) ]]запускается jobsв отдельной подоболочке, но я не уверен в деталях. У меня естьразместил вопрособ этом на U&L, если кому-то интересно.

решение3

Вы можете использовать waitкоманду, которая ждет остановки фоновых заданий.

Пример:

$ ping -c 10 localhost &>/dev/null &
[29787]
$ wait
[1]+  Done ping -c 10 localhost &>/dev/null

Команда wait блокируется, а по pingзавершении отпускается, после чего выводится сообщение.

Вы можете использовать is с несколькими командами:

$ ping -c 5 localhost &>/dev/null & ping -c 5 facebook.com  &>/dev/null &
[1] 29867
[2] 29868
$ wait
[1]-  Done                    ping -c 5 localhost &>/dev/null
[2]+  Done                    ping -c 5 facebook.com &>/dev/null

Связанный контент