Ответ 1
Здесь более простое решение - просто добавьте следующую строку вверху вашего script:
trap "kill 0" SIGINT
Killing 0
отправляет сигнал всем процессам в текущей группе процессов.
У меня есть bash script, чтобы проверить, как сервер работает под нагрузкой.
num=1
if [ $# -gt 0 ]; then
num=$1
fi
for i in {1 .. $num}; do
(while true; do
{ time curl --silent 'http://localhost'; } 2>&1 | grep real
done) &
done
wait
Когда я нажимаю Ctrl-C, основной процесс завершается, но фоновые петли продолжают работать. Как заставить их всех выйти? Или существует лучший способ создания настраиваемого числа логических циклов, выполняемых параллельно?
Здесь более простое решение - просто добавьте следующую строку вверху вашего script:
trap "kill 0" SIGINT
Killing 0
отправляет сигнал всем процессам в текущей группе процессов.
Вам нужно использовать контроль заданий, который, к сожалению, немного сложнее. Если это единственные фоновые задания, которые, как вы ожидаете, будут запущены, вы можете запустить команду, подобную этой:
jobs \
| perl -ne 'print "$1\n" if m/^\[(\d+)\][+-]? +Running/;' \
| while read -r ; do kill %"$REPLY" ; done
jobs
печатает список всех активных заданий (запуск заданий, а также недавно завершенных или завершенных заданий) в следующем формате:
[1] Running sleep 10 &
[2] Running sleep 10 &
[3] Running sleep 10 &
[4] Running sleep 10 &
[5] Running sleep 10 &
[6] Running sleep 10 &
[7] Running sleep 10 &
[8] Running sleep 10 &
[9]- Running sleep 10 &
[10]+ Running sleep 10 &
(Это те задания, которые я запускал при запуске for i in {1..10} ; do sleep 10 & done
.)
perl -ne ...
Я использую Perl для извлечения числа заданий рабочих заданий; вы можете использовать другой инструмент, если хотите. Возможно, вам потребуется изменить этот script, если ваш jobs
имеет другой формат вывода; но вышеупомянутый вывод также находится на Cygwin, поэтому он очень вероятно совпадает с вашим.
read -r
читает "сырую" строку со стандартного ввода и сохраняет ее в переменной $REPLY
. kill %"$REPLY"
будет чем-то вроде kill %1
, который "убивает" (отправляет сигнал прерывания) номер задания 1. (Не путать с kill 1
, который убьет номер процесса 1.) Вместе, while read -r ; do kill %"$REPLY" ; done
идет через каждый номер задания, напечатанный Perl script, и убивает его.
Кстати, ваш for i in {1 .. $num}
не будет делать то, что вы ожидаете, так как расширение расширений обрабатывается до расширения параметра, поэтому у вас есть эквивалент for i in "{1" .. "$num}"
. Во всяком случае, вы не можете иметь пустое пространство внутри расширения скобы.) К сожалению, я не знаю чистой альтернативы; Я думаю, вам нужно сделать что-то вроде for i in $(bash -c "{1..$num}")
, или переключиться на арифметику for
-loop или whatnot.
Также, кстати, вам не нужно вставлять while-loop в круглые скобки; &
уже заставляет задание запускаться в подоболочке.
Один способ убить подоболочки, но не сам:
kill $(jobs -p)
Здесь мое возможное решение. Я отслеживаю идентификаторы процессов подоболочки, используя переменную массива, и захватывая сигнал Ctrl-C, чтобы убить их.
declare -a subs #array of subshell pids
function kill_subs() {
for pid in ${subs[@]}; do
kill $pid
done
exit 0
}
num=1 if [ $# -gt 0 ]; then
num=$1 fi
for ((i=0;i < $num; i++)); do
while true; do
{ time curl --silent 'http://localhost'; } 2>&1 | grep real
done &
subs[$i]=$! #grab the pid of the subshell
done
trap kill_subs 1 2 15
wait