Как мне завершить все процессы подоболочки? - PullRequest
29 голосов
/ 03 декабря 2011

У меня есть скрипт bash для проверки работы сервера под нагрузкой.

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

Ответы [ 4 ]

43 голосов
/ 03 декабря 2011

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

trap "kill 0" SIGINT

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

4 голосов
/ 31 июля 2015

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

kill $(jobs -p)
2 голосов
/ 03 декабря 2011

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

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 для извлечения номеров работ из запущенных работ; очевидно, вы можете использовать другой инструмент, если хотите. Вам может потребоваться изменить этот скрипт, если ваш jobs имеет другой формат вывода; но вышеприведенный вывод также относится к Cygwin, поэтому он, скорее всего, идентичен вашему.

read -r считывает «необработанную» строку из стандартного ввода и сохраняет ее в переменную $REPLY. kill %"$REPLY" будет что-то вроде kill %1, который "убивает" (посылает сигнал прерывания) задание номер 1. (Не путать с kill 1, что приведет к уничтожению процесса номер 1.) Вместе while read -r ; do kill %"$REPLY" ; done проходит через каждый номер задания, напечатанный скриптом Perl, и убивает его.

Кстати, ваш for i in {1 .. $num} не будет делать то, что вы ожидаете, поскольку расширение скобки обрабатывается до расширения параметра, поэтому то, что у вас есть, эквивалентно for i in "{1" .. "$num}". (И, во всяком случае, у вас не может быть пустого пространства внутри расширения скобок.) К сожалению, я не знаю чистой альтернативы; Я думаю, что вы должны сделать что-то вроде for i in $(bash -c "{1..$num}"), или переключиться на арифметический for -петл или еще много чего.

Кстати, вам не нужно заключать цикл в круглые скобки; & уже вызывает выполнение задания в подоболочке.

0 голосов
/ 03 декабря 2011

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