Linux ожидание окончания запущенного процесса из shell

Обновлено: 05.07.2024


Команда wait используется для ожидания определенного идентификатора процесса и идентификатора задания и возврата их статуса завершения.

При выполнении большого потока автоматизации нам нужно сделать так, чтобы несколько модулей ожидали завершения предыдущего набора модулей и возвращали данные, конвейеризованные в следующие модули, в этом случае мы можем использовать команду Wait до завершения предыдущего модуля.

Как работает команда wait

Команда wait, используемая для наблюдения за предыдущим процессом, зависит от состояния возврата предыдущего процесса и возвращает статус выхода.

Например, если мы хотим дождаться завершения определенного идентификатора процесса 13245, то мы должны использовать «ждать 13245», когда процесс 13245 завершает команду ожидания, возвращающую значения 13245 состояния выхода.

Значение состояния выхода команды ожидания зависит от последнего указанного PID / JID.

Если какой-либо процесс завершается ненормально, состояние выхода будет больше 128.

Команда ожидания завершается со значением 0, когда она вызывает без дочернего процесса, и все идентификаторы процесса, известные текущей оболочке, завершены.

Если команда wait обнаруживает какую-либо ошибку, она возвращает любое значение от 1 до 126 .

Если последний идентификатор процесса неизвестен, команда ожидания завершается со значением 127.

Примеры команд wait

Давайте проверим несколько скриптов, чтобы понять, как работает команда wait.

У нас есть два скрипта с именами «foo.sh» и «bar.sh».

У нас есть два скрипта с именами «foo.sh» и «bar.sh».

Наконец, он возвращает код выхода скрипта foo.sh

Заключение

Давайте посмотрим разницу между командами wait и sleep.

Wait: когда пользователь хочет остановить текущий процесс, он освобождает все ресурсы, удерживаемые процессом, и ожидает выполнения другого процесса.

Нам нужно использовать notify, чтобы этот процесс знал, что надо начать выполнение снова после завершения другого процесса.

Sleep: этот системный вызов используется тогда, когда пользователь хочет на некоторое время остановить текущий процесс. Он удерживает блокировки ресурсов до истечения времени ожидания и снова запускает выполнение процесса. Здесь процесс имеет контроль на протяжении всего выполнения. Например, я выполнил некоторую команду в bash и хочу некоторое время уходить в sleep, так как ожидаю некоторого вывода от выполненной команды, которая будет использоваться для дальнейшего выполнения текущего процесса.

image


В прошлый раз мы говорили о работе с потоками ввода, вывода и ошибок в bash-скриптах, о дескрипторах файлов и о перенаправлении потоков. Сейчас вы знаете уже достаточно много для того, чтобы писать что-то своё. На данном этапе освоения bash у вас вполне могут возникнуть вопросы о том, как управлять работающими скриптами, как автоматизировать их запуск.

До сих пор мы вводили имена скриптов в командную строку и нажимали Enter, что приводило к немедленному запуску программ, но это — не единственный способ вызова сценариев. Сегодня мы поговорим о том как скрипт может работать с сигналами Linux, о различных подходах к запуску скриптов и к управлению ими во время работы.

Сигналы Linux

В Linux существует более трёх десятков сигналов, которые генерирует система или приложения. Вот список наиболее часто используемых, которые наверняка пригодятся при разработке сценариев командной строки.

Код сигнала
Название
Описание
1
SIGHUP
Закрытие терминала
2
SIGINT
Сигнал остановки процесса пользователем с терминала (CTRL + C)
3
SIGQUIT
Сигнал остановки процесса пользователем с терминала (CTRL + \) с дампом памяти
9
SIGKILL
Безусловное завершение процесса
15
SIGTERM
Сигнал запроса завершения процесса
17
SIGSTOP
Принудительная приостановка выполнения процесса, но не завершение его работы
18
SIGTSTP
Приостановка процесса с терминала (CTRL + Z), но не завершение работы
19
SIGCONT
Продолжение выполнения ранее остановленного процесса

Если оболочка bash получает сигнал SIGHUP когда вы закрываете терминал, она завершает работу. Перед выходом она отправляет сигнал SIGHUP всем запущенным в ней процессам, включая выполняющиеся скрипты.

Сигнал SIGINT приводит к временной остановке работы. Ядро Linux перестаёт выделять оболочке процессорное время. Когда это происходит, оболочка уведомляет процессы, отправляя им сигнал SIGINT .

Bash-скрипты не контролируют эти сигналы, но они могут распознавать их и выполнять некие команды для подготовки скрипта к последствиям, вызываемым сигналами.

Отправка сигналов скриптам

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

Завершение работы процесса

Комбинация клавиш CTRL + C генерирует сигнал SIGINT и отправляет его всем процессам, выполняющимся в оболочке, что приводит к завершению их работы.

Выполним в оболочке такую команду:


После этого завершим её работу комбинацией клавиш CTRL + C .


Завершение работы процесса с клавиатуры

Временная остановка процесса

Комбинация клавиш CTRL + Z позволяет сгенерировать сигнал SIGTSTP , который приостанавливает работу процесса, но не завершает его выполнение. Такой процесс остаётся в памяти, его работу можно возобновить. Выполним в оболочке команду:


И временно остановим её комбинацией клавиш CTRL + Z .


Приостановка процесса

Число в квадратных скобках — это номер задания, который оболочка назначает процессу. Оболочка рассматривает процессы, выполняющиеся в ней, как задания с уникальными номерами. Первому процессу назначается номер 1, второму — 2, и так далее.

Если вы приостановите задание, привязанное к оболочке, и попытаетесь выйти из неё, bash выдаст предупреждение.

Просмотреть приостановленные задания можно такой командой:


Список заданий

В колонке S , выводящей состояние процесса, для приостановленных процессов выводится T . Это указывает на то, что команда либо приостановлена, либо находится в состоянии трассировки.

Если нужно завершить работу приостановленного процесса, можно воспользоваться командой kill . Подробности о ней можно почитать здесь.

Выглядит её вызов так:

Перехват сигналов

Для того, чтобы включить в скрипте отслеживание сигналов Linux, используется команда trap . Если скрипт получает сигнал, указанный при вызове этой команды, он обрабатывает его самостоятельно, при этом оболочка такой сигнал обрабатывать не будет.

Команда trap позволяет скрипту реагировать на сигналы, в противном случае их обработка выполняется оболочкой без его участия.

Рассмотрим пример, в котором показано, как при вызове команды trap задаётся код, который надо выполнить, и список сигналов, разделённых пробелами, которые мы хотим перехватить. В данном случае это всего один сигнал:


Перехват сигналов

Каждый раз, когда вы нажимаете клавиши CTRL + C , скрипт выполняет команду echo , указанную при вызове trace вместо того, чтобы позволить оболочке завершит его работу.

Перехват сигнала выхода из скрипта

Перехватить сигнал выхода из скрипта можно, использовав при вызове команды trap имя сигнала EXIT :


Перехват сигнала выхода из скрипта

При выходе из скрипта, будь то нормальное завершение его работы или завершение, вызванное сигналом SIGINT , сработает перехват и оболочка исполнит команду echo .

Модификация перехваченных сигналов и отмена перехвата

Для модификации перехваченных скриптом сигналов можно выполнить команду trap с новыми параметрами:


Модификация перехвата сигналов

После модификации сигналы будут обрабатываться по-новому.

Перехват сигналов можно и отменить, для этого достаточно выполнить команду trap , передав ей двойное тире и имя сигнала:


Если скрипт получит сигнал до отмены перехвата, он обработает его так, как задано в действующей команде trap . Запустим скрипт:


И нажмём CTRL + C на клавиатуре.


Сигнал, перехваченный до отмены перехвата

Первое нажатие CTRL + C пришлось на момент исполнения скрипта, когда перехват сигнала был в силе, поэтому скрипт исполнил назначенную сигналу команду echo . После того, как исполнение дошло до команды отмены перехвата, команда CTRL + C сработала обычным образом, завершив работу скрипта.

Выполнение сценариев командной строки в фоновом режиме

Иногда bash-скриптам требуется немало времени для выполнения некоей задачи. При этом вам может понадобиться возможность нормально работать в командной строке, не дожидаясь завершения скрипта. Реализовать это не так уж и сложно.

Если вы видели список процессов, выводимый командой ps , вы могли заметить процессы, которые выполняются в фоне и не привязаны к терминалу.
Напишем такой скрипт:


Запустим его, указав после имени символ амперсанда ( & ):


Это приведёт к тому, что он будет запущен как фоновый процесс.


Запуск скрипта в фоновом режиме


Список процессов

При таком подходе, если выйти из терминала, скрипт, выполняющийся в фоне, так же завершит работу.

Что если нужно, чтобы скрипт продолжал работать и после закрытия терминала?

Выполнение скриптов, не завершающих работу при закрытии терминала

Скрипты можно выполнять в фоновых процессах даже после выхода из терминальной сессии. Для этого можно воспользоваться командой nohup . Эта команда позволяет запустить программу, блокируя сигналы SIGHUP , отправляемые процессу. В результате процесс будет исполняться даже при выходе из терминала, в котором он был запущен.

Применим эту методику при запуске нашего скрипта:


Вот что будет выведено в терминал.


Команда nohup

Обратите внимание на то, что при запуске нескольких скриптов из одной и той же директории то, что они выводят, попадёт в один файл nohup.out .

Просмотр заданий

Команда jobs позволяет просматривать текущие задания, которые выполняются в оболочке. Напишем такой скрипт:


И временно остановим комбинацией клавиш CTRL + Z .


Запуск и приостановка скрипта

Запустим тот же скрипт в фоновом режиме, при этом перенаправим вывод скрипта в файл так, чтобы он ничего не выводил на экране:


Выполнив теперь команду jobs , мы увидим сведения как о приостановленном скрипте, так и о том, который работает в фоне.


Получение сведений о скриптах

Ключ -l при вызове команды jobs указывает на то, что нам нужны сведения об ID процессов.

Перезапуск приостановленных заданий

Для того, чтобы перезапустить скрипт в фоновом режиме, можно воспользоваться командой bg .


Нажмём CTRL + Z , что временно остановит его выполнение. Выполним следующую команду:


Теперь скрипт выполняется в фоновом режиме.

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

Для перезапуска задания в обычном режиме воспользуйтесь командой fg :

Планирование запуска скриптов

Linux предоставляет пару способов запуска bash-скриптов в заданное время. Это команда at и планировщик заданий cron .

Вызов команды at выглядит так:


Эта команда распознаёт множество форматов указания времени.

  • Стандартный, с указанием часов и минут, например — 10:15.
  • С использованием индикаторов AM/PM, до или после полудня, например — 10:15PM.
  • С использованием специальных имён, таких, как now , noon , midnight .
  • Стандартный формат указания даты, при котором дата записывается по шаблонам MMDDYY , MM/DD/YY , или DD.MM.YY .
  • Текстовое представление даты, например, Jul 4 или Dec 25 , при этом год можно указать, а можно обойтись и без него.
  • Запись вида now + 25 minutes .
  • Запись вида 10:15PM tomorrow .
  • Запись вида 10:15 + 7 days .


Планирование заданий с использованием команды at

Ключ -M при вызове at используется для отправки того, что выведет скрипт, по электронной почте, если система соответствующим образом настроена. Если отправка электронного письма невозможна, этот ключ просто подавит вывод.

Для того чтобы посмотреть список заданий, ожидающих выполнения, можно воспользоваться командой atq :


Список заданий, ожидающих выполнения

Удаление заданий, ожидающих выполнения

Удалить задание, ожидающее выполнения, позволяет команда atrm . При её вызове указывают номер задания:


Удаление задания

Запуск скриптов по расписанию

Планирование однократного запуска скриптов с использованием команды at способно облегчить жизнь во многих ситуациях. Но как быть, если нужно, чтобы скрипт выполнялся в одно и то же время ежедневно, или раз в неделю, или раз в месяц?

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

Crontab выполняется в фоне и, основываясь на данных в так называемых cron-таблицах, запускает задания по расписанию.

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


При планировании запуска скрипта по расписанию crontab принимает данные о том, когда нужно выполнить задание, в таком формате:


Например, если надо, чтобы некий скрипт с именем command выполнялся ежедневно в 10:30, этому будет соответствовать такая запись в таблице заданий:


Здесь универсальный символ « * », использованный для полей, задающих день месяца, месяц и день недели, указывает на то, что cron должен выполнять команду каждый день каждого месяца в 10:30.

Если, например, надо, чтобы скрипт запускался в 4:30PM каждый понедельник, понадобится создать в таблице заданий такую запись:


Нумерация дней недели начинается с 0, 0 означает воскресенье, 6 — субботу. Вот ещё один пример. Здесь команда будет выполняться в 12 часов дня в первый день каждого месяца.


Нумерация месяцев начинается с 1.
Для того чтобы добавить запись в таблицу, нужно вызвать crontab с ключом -e :


Затем можно вводить команды формирования расписания:


Благодаря этой команде скрипт будет вызываться ежедневно в 10:30. Если вы столкнётесь с ошибкой «Resource temporarily unavailable», выполните нижеприведённую команду с правами root-пользователя:


Организовать периодический запуск скриптов с использованием cron можно ещё проще, воспользовавшись несколькими специальными директориями:


Если поместить файл скрипта в одну из них, это приведёт, соответственно, к его ежечасному, ежедневному, еженедельному или ежемесячному запуску.

Запуск скриптов при входе в систему и при запуске оболочки

Автоматизировать запуск скриптов можно, опираясь на различные события, такие, как вход пользователя в систему или запуск оболочки. Тут можно почитать о файлах, которые обрабатываются в подобных ситуациях. Например, это следующие файлы:


Для того, чтобы запускать скрипт при входе в систему, поместите его вызов в файл .bash_profile .

А как насчёт запуска скриптов при открытии терминала? Организовать это поможет файл .bashrc .

Итоги

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

Есть ли в bash встроенная функция для ожидания завершения процесса вроде wait(pid) , но для не-дочернего процесса?

Если нет, то каким образом можно дожидатся окончания процесса?

Интересует не механический способ проверки циклом, а что-то вроде прерываний.


66k 154 154 золотых знака 66 66 серебряных знаков 203 203 бронзовых знака


Я предполагаю, что автора интересует не произвольный процесс, а некий демон. Если этот демон "честно" создаёт pid-файл, то можно попробовать навесить inotify на удаление этого файла. Тогда, при завершении процесса демона, он будет удалять свой pid-файл, а ОС пошлёт Вам соответствующий сигнал.

Но, как очевидно, при аварийном падении демона этот фокус не пройдёт.


Вам нужен цикл ожидания, очевидно же :)


автор вопроса ведь обговорил этом момент, про циклы: Интересует не механический способ проверки циклом

Как вариант можно предложить скрипт, который в цикле отслеживает заданный PID и посылает вызывающему скрипту (его PID передается вторым аргументом) сигнал.

И пример вызывающего скрипта:

Следует учесть, что сигнал тут будет обработан в read (поскольку это встроенная команда), однако выход по прерыванию не происходит. Если же скрипт вызвал какую-либо внешнюю команду (grep, cat . ), то обработка сигнала произойдет после ее завершения.

Если воспользоваться ответом @alexanderbarakin (и strace у вас работает (по крайней мере мне в Ubuntu пришлось от рута выполнить echo 0 >/proc/sys/kernel/yama/ptrace_scope , чтобы она заработала для отслеживания произвольного процесса с тем же UID)), то цикл ожидания в скрипте wdog.sh можно заменить на

43.4k 4 4 золотых знака 41 41 серебряный знак 109 109 бронзовых знаков

в ответах на вопрос WAIT for “any process” to finish приведены некоторые соображения по этому поводу: какого-либо встроенного (или даже просто заведомо работоспособного) средства, конечно, нет.

понятно, что «следить» за процессами, запущенными другими пользователями, не получится, пока не запустите strace от имени пользователя root.


66k 154 154 золотых знака 66 66 серебряных знаков 203 203 бронзовых знака

вот из этого мануала я узнал следующее:

Для досрочного завершения потока можно воспользоваться функцией pthread_cancel. Единственным аргументом этой функции является идентификатор потока. Функция pthread_cancel возвращает 0 в случае успеха и ненулевое значение(код ошибки) в случае ошибки.

Функция pthread_setcancelstate определяет, будет ли поток реагировать на обращение к нему с помощью pthread_cancel.

То есть получается надо отменить удаление функцией pthread_cancel с помощью pthread_setcancelstate и анализировать какие коды возвращает pthread_cancel , и скорей всего она будет возвращать какой то код ошибки типа поток не найден если его нет и при всем при этом она этот поток не сможет завершить.

Favorite

Добавить в избранное

Bash Heredoc

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

В этой статье мы рассмотрим встроенную команду wait в Bash.

Команда wait в Bash

Общий синтаксис wait встроенного модуля имеет следующий вид:

Команда wait возвращает статус выхода последней ожидаемой команды.

Например, чтобы дождаться фонового процесса с PID 7654, вы должны использовать:

Если задано несколько процессов, команда ожидает завершения всех процессов.

Задания указываются с использованием спецификации задания («jobspec»), которая является способом ссылки на процессы, составляющие задание. Спецификация задания начинается с символа процента, за которым следует номер задания ( %n). Вот пример:

Выполните команду в фоновом режиме :

Идентификатор задания оболочки (в скобках) и идентификатор процесса будут отображаться на вашем терминале:

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

При вызове с параметром -n команда ожидает завершения только одного задания из заданных pid или заданий и возвращает статус завершения. Если аргументы не указаны, wait -n ожидает завершения любого фонового задания и возвращает статус завершения задания.

В приведенном выше примере выводится wait -nтолько статус возврата задания, которое завершается первым; он не показывает PID задания. Если вы хотите получить идентификатор задания или спецификацию задания, для которого возвращается статус выхода, используйте параметр -p, чтобы назначить его переменной:

Опция -p была представлена ​​в Bash 5.1. Если вы используете старую версию Bash, вы получите ошибку “invalid option”.

Параметр -f сообщает, что wait нужно дождаться фактического завершения каждого pid или jobpec, прежде чем возвращать свой код выхода, а не возвращаться при изменении статуса задания. Эта опция действительна, только если включено управление заданиями. По умолчанию управление заданиями включено только для интерактивных подсказок.

Примеры

wait обычно используется в сценариях оболочки, которые порождают дочерние процессы, выполняющиеся параллельно.

Чтобы проиллюстрировать, как работает команда, создайте следующий сценарий:

Давайте объясним код построчно:

  1. Первая строка называется shebang и сообщает операционной системе, какой интерпретатор использовать для анализа остальной части файла.
  2. Мы используем команду sleep для имитации трудоемкого фонового процесса.
  3. $!- это внутренняя переменная Bash, в которой хранится PID последнего задания, запущенного в фоновом режиме. В этом примере это PID команды sleep. Мы сохраняем PID в переменной ( process_id).
  4. Печатает номер PID.
  5. PID передается waitкоманде, которая ожидает завершения команды sleep.
  6. Печатает статус выхода командыwait. $?- внутренняя переменная Bash, которая содержит статус выхода последней выполненной команды.

Если вы запустите сценарий, он напечатает что-то вроде этого:

Вот пример использования опции -n:

Когда скрипт выполняется, он запускает 3 фоновых процесса. wait -n ожидает завершения первого задания и вывода инструкции echo. waitожидает завершения всех дочерних фоновых заданий.

Последний пример объясняет этот вариант -f. Откройте терминал и запустите:

Откройте другой терминал и остановите процесс командой kill:

После изменения статуса процесса команда wait завершится и вернет код завершения процесса.

Теперь повторите те же шаги, но на этот раз используйте wait -f $pid:

Остановите процесс с другого терминала:

На этот раз команда wait не будет выполнена. Он будет работать до тех пор, пока процесс sleep не завершится.

Заключение

Команда wait ожидает завершения указанных заданий и возвращает код завершения задания.

Если у вас есть какие-либо вопросы или отзывы, не стесняйтесь оставлять комментарии.

Читать 5 практических примеров команды Tail в Linux

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Читайте также: