У меня есть сценарий-оболочка Bash, который запускает сложный сценарий моделирования, который, в свою очередь, запускает несколько подпроцессов и собственных сценариев. Я хочу выяснить, как отслеживать все процессы, порождаемые одним запуском сценария моделирования, чтобы уничтожать их все при выполнении определенных критериев.
Например, мой скрипт-оболочка под названием pipeline_runner.sh
делает следующее:
#!/bin/bash
# Some set up of the script ...
./monitor_job.sh ... arguments TBD ... &
script_path = "path/to/bash/script"
chmod u+x "$script_path"
"$script_path"
# ...
Каждый запуск pipeline_runner.sh
запускает экземпляр monitor_job.sh
в фоновом режиме для отслеживания конкретного запуска path/to/bash/script
, запущенного этим запуском pipeline_runner.sh
. Когда какое-то произвольное условие, определенное в monitor_job.sh
, выполняется, оно должно иметь возможность уничтожить этот конкретный запуск path/to/bash/script
вместе со всеми процессами, прямо или косвенно запущенными им.
Множество других процессов, запускаемых запуском path/to/bash/script
, многочисленны и различаются, поэтому я пытаюсь выяснить, как захватить каждый скрипт, который создается в результате запуска, в какую-то группу или список и иметь возможность уничтожить их все, когда это необходимо. Уничтожить только начальный процесс $script_path
недостаточно, поскольку все подпроцессы этого сценария сохранятся.
Важными второстепенными целями являются:
сделать эту динамику такой, чтобы она не зависела от того, какой сценарий обозначен $script_path
. Это означает, что я не могу просто жестко закодировать конкретные имена команд для поиска.
осуществлять мониторинг в отдельном скрипте (monitor_job.sh
), как описано, а не напрямую в pipeline_runner.sh
.
Как я могу отслеживать все процессы, запускаемые скриптом моделирования, чтобы иметь возможность убить их все при необходимости?
🤔 А знаете ли вы, что...
Bash предоставляет мощные механизмы для автоматизации задач в командной строке.
kill -- -$$
завершит всю группу процессов.
Например, в следующем скрипте мы запускаем 2 подпроцесса, sleep 15
и sleep 30
, тогда у нас могут быть другие задачи для запуска (в данном случае sleep 5
), и поскольку мы удовлетворяем нашим критериям выхода, мы можем убить всю группу процессов.
#!/bin/sh
echo "Parent pid $$"
sleep 15 &
echo "child 1 pid $!"
sleep 30 &
echo "child 2 pid $!"
sleep 5
echo "criteria met"
kill -- -$$
Если мы запустим это с bash test.sh ; ps -ef | grep sleep
, мы получим:
$ bash test.sh ; ps -ef | grep sleep
Parent pid 87546
child 1 pid 87547
child 2 pid 87548
criteria met
Terminated: 15
501 87595 789 0 2:08PM ttys007 0:00.00 grep sleep
Таким образом, мы видим, что подпроцессы также были убиты.
Проблема с этим подходом в том, что если бы сочетание клавиш Ctrl+C было введено сразу после моего выполнения, мы бы получили:
$ bash test.sh ; ps -ef | grep sleep
Parent pid 88352
child 1 pid 88353
child 2 pid 88354
^C
501 88353 1 0 2:10PM ttys007 0:00.00 sleep 15
501 88354 1 0 2:10PM ttys007 0:00.00 sleep 30
501 88391 789 0 2:10PM ttys007 0:00.00 grep sleep
Это означает, что различные подпроцессы будут продолжать работать как «сироты» (принятые процессом инициализации).
Чтобы решить эту проблему, мы могли бы использовать trap
и изменить наш скрипт на:
#!/bin/sh
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
echo "Parent pid $$"
sleep 15 &
echo "child 1 pid $!"
sleep 30 &
echo "child 2 pid $!"
sleep 5
echo "criteria met"
exit 0
Обычное выполнение останется прежним, потому что exit 0
будет пойман ловушкой и, в свою очередь, kill -- -$$
будет выполнен, как и раньше.
Теперь, если мы запустим наш скрипт и введем ctrl+c сразу после выполнения, на этот раз мы получим:
Parent pid 91578
child 1 pid 91579
child 2 pid 91580
^CTerminated: 15
501 91590 789 0 2:18PM ttys007 0:00.00 grep sleep
где мы видим, что подпроцесс также был убит.