bash shutdown hook; или убить все фоновые процессы, когда основной процесс убит
у меня есть bash, который запускает бесконечные команды в качестве фоновых процессов:
#!/bin/bash
function xyz() {
# some awk command
}
endlesscommand "param 1" | xyz & # async
pids=$!
endlesscommand "param 2" | xyz & # async
pids="$pids "$!
endlesscommand "param 3" | xyz # sync so the script doesn't leave
единственный способ остановить этот скрипт - (должен быть) Ctrl-C или kill, и когда это произойдет, мне нужно убить все фоновые процессы, перечисленные в переменной $pids.
Как мне это сделать?
Если бы можно было поймать сигнал kill на основном процессе и выполнить функцию, когда это произойдет (крючок выключения), я бы сделал что-то вроде:
for $pid in $pids; do kill $pid; done;
Но Я не могу найти как это сделать...
3 ответов
вот ловушка, которая не нуждается в вас, чтобы отслеживать pids
:
trap 'jobs -p | xargs kill' EXIT
редактировать: @Barmar спросил, работает ли это в сценариях без источников, где управление заданиями обычно недоступно. Так и есть. Рассмотрим такой скрипт:
$ cat no-job-control
#! /bin/bash
set -e -o pipefail
# Prove job control is off
if suspend
then
echo suspended
else
echo suspension failed, job control must be off
fi
echo
# Set up the trap
trap 'jobs -p | xargs kill' EXIT
# Make some work
(echo '=> Starting 0'; sleep 5; echo '=> Finishing 0') &
(echo '=> Starting 1'; sleep 5; echo '=> Finishing 1') &
(echo '=> Starting 2'; sleep 5; echo '=> Finishing 2') &
echo "What's in jobs -p?"
echo
jobs -p
echo
echo "Ok, exiting now"
echo
при запуске мы видим pids трех лидеров группы, а затем увидеть их убитыми:
$ ./no-job-control
./no-job-control: line 6: suspend: cannot suspend: no job control
suspension failed, job control must be off
=> Starting 0
What's in jobs -p?
=> Starting 1
54098
54099
54100
Ok, exiting now
=> Starting 2
./no-job-control: line 31: 54098 Terminated: 15 ( echo '=> Starting 0'; sleep 5; echo '=> Finishing 0' )
./no-job-control: line 31: 54099 Terminated: 15 ( echo '=> Starting 1'; sleep 5; echo '=> Finishing 1' )
./no-job-control: line 31: 54100 Terminated: 15 ( echo '=> Starting 2'; sleep 5; echo '=> Finishing 2' )
если мы закомментировать trap
line и повторный запуск, три задания не умирают и фактически распечатывают их окончательный сообщения через несколько секунд. Обратите внимание на возвращаемое приглашение, чередующееся с окончательными выводами.
$ ./no-job-control
./no-job-control: line 6: suspend: cannot suspend: no job control
suspension failed, job control must be off
=> Starting 0
What's in jobs -p?
54110
54111
54112
=> Starting 1
Ok, exiting now
=> Starting 2
$ => Finishing 0
=> Finishing 2
=> Finishing 1
Вы можете использовать pgrep и функцию, чтобы убить все процессы, созданные в рамках основного процесса, как это. Это убило бы не только прямые дочерние процессы, но и те, которые были созданы под ним.
#!/bin/bash
function killchildren {
local LIST=() IFS=$'\n' A
read -a LIST -d '' < <(exec pgrep -P "")
local A SIGNAL="${2:-SIGTERM}"
for A in "${LIST[@]}"; do
killchildren_ "$A" "$SIGNAL"
done
}
function killchildren_ {
local LIST=()
read -a LIST -d '' < <(exec pgrep -P "")
kill -s "" ""
if [[ ${#LIST[@]} -gt 0 ]]; then
local A
for A in "${LIST[@]}"; do
killchildren_ "$A" ""
done
fi
}
trap 'killchildren "$BASHPID"' EXIT
endlesscommand "param 1" &
endlesscommand "param 2" &
endlesscommand "param 3" &
while pgrep -P "$BASHPID" >/dev/null; do
wait
done
Что касается вашего исходного кода, было бы лучше просто использовать массивы, и Вам также не нужно использовать цикл for:
#!/bin/bash
trap 'kill "${pids[@]}"' EXIT
pids=()
endlesscommand "param 1" & # async
pids+=("$!")
endlesscommand "param 2" & # async
pids+=("$!")
endlesscommand "param 3" & # syncing this is not a good idea since if the main process would end along with it if it ends earlier.
pids+=("$!")
while pgrep -P "$BASHPID" >/dev/null; do
wait
done
исходная ссылка на функцию: http://www.linuxquestions.org/questions/blog/konsolebox-210384/bash-functions-to-list-and-kill-or-send-signals-to-process-trees-34624/
kill `ps axl | grep "endlesscommand" | awk '{printf " "}'`
Это будет искать родительские процессы, влияющие на "endlesscommand"