Подтвердить что ты не робот

Как завершить все процессы подоболочки?

У меня есть 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, основной процесс завершается, но фоновые петли продолжают работать. Как заставить их всех выйти? Или существует лучший способ создания настраиваемого числа логических циклов, выполняемых параллельно?

4b9b3361

Ответ 1

Здесь более простое решение - просто добавьте следующую строку вверху вашего script:

trap "kill 0" SIGINT

Killing 0 отправляет сигнал всем процессам в текущей группе процессов.

Ответ 2

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

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 в круглые скобки; & уже заставляет задание запускаться в подоболочке.

Ответ 3

Один способ убить подоболочки, но не сам:

kill $(jobs -p)

Ответ 4

Здесь мое возможное решение. Я отслеживаю идентификаторы процессов подоболочки, используя переменную массива, и захватывая сигнал 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