Тихо убить подоболочку?

Тихо убить подоболочку?

Я хочу реализовать что-то вроде этогоВопрос/Ответно для под-оболочки. Вот минимальный пример того, что я пытаюсь сделать:

(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)

echo subshell done

Как сделать так, чтобы subshell doneвозвращалось только:

./test.sh: line 4:  5439 Terminated              ( subshell=$BASHPID; ( kill $subshell && wait $subshell 2> /dev/null ) & sleep 600 )
subshell done

Редактировать: Я могу ошибаться в терминологии, под подоболочкой я подразумеваю процесс внутри первого набора скобок.

Обновлять:

Я хочу опубликовать фрагмент из реальной программы для контекста, выше приведено упрощение:

# If subshell below if killed or returns error connected variable won't be set
(if [ -n "$2" ];then

      # code to setup wpa configurations here

      # If wifi key is wrong kill subshell
      subshell=$BASHPID
      (sudo stdbuf -o0 wpa_supplicant -Dwext -i$wifi -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 \
        | grep -m 1 "pre-shared key may be incorrect" \
        && kill -s PIPE "$subshell") &

      # More code which does the setup necessary for wifi

) && connected=true

# later json will be returned based on if connected is set

решение1

Примечание:

  • wait $subshellне будет работать, так как $subshellне является дочерним процессом запущенного вами процесса wait. В любом случае, вы не ждете, пока процесс выполнит это, waitтак что это не имеет большого значения.
  • kill $subshellсобирается убить подоболочку, но не sleepесли подоболочка успела запустить его к моменту killзапуска. Однако вы можете запустить sleepв том же процессе сexec
  • вы можете использовать SIGPIPE вместо SIGTERM, чтобы избежать сообщения
  • оставление переменной без кавычек в списочных контекстах имеет совершенно особое значение в bash.

Итак, сказав все это, вы можете сделать:

(
  subshell=$BASHPID
  kill -s PIPE "$subshell" &
  sleep 600
)
echo subshell done

(замените sleep 60на exec sleep 60, если вы хотите killзавершить процесс sleep, а не только подоболочку, которая в этом случае может даже не успеть запуститься sleepк тому времени, как вы ее завершите).

В любом случае, я не совсем понимаю, чего вы хотите этим добиться.

sleep 600 &

будет более надежным способом запуска sleepв фоновом режиме, если это то, что вы хотите сделать (или (sleep 600 &)если вы хотите скрыть этот sleepпроцесс от основной оболочки)

Теперь с вашим реальным

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf

команда, обратите внимание, что sudoона порождает дочерний процесс для запуска команды (хотя бы потому, что ей может потребоваться зарегистрировать свое состояние или выполнить некоторые задачи сеанса PAM впоследствии). stdbufОднако будет выполняться wpa_supplicantв том же процессе, поэтому в итоге у вас будет три процесса (в дополнение к остальной части скрипта) в wpa_supplicantродословной:

  1. подоболочка
  2. sudo как потомок 1
  3. wpa_supplicant (который ранее запускал stdbuf) как дочерний элемент 2

Если вы убьете 1, это не приведет к автоматическому уничтожению 2. Однако если вы убьете 2, если только это не будет сделано с помощью сигнала вроде SIGKILL, который невозможно перехватить, это убьет 3, поскольку он sudoперенаправляет полученные сигналы команде, которую он запускает.

В любом случае, это не та подоболочка, которую вам хотелось бы здесь убить, их 3 или, по крайней мере, 2.

Теперь, если он запущен, rootа остальная часть скрипта — нет, вы не сможете так легко его завершить.

Вам нужно, чтобы это killбыло сделано как root, поэтому вам нужно:

sudo WIFI="$wifi" bash -c '
  (echo "$BASHPID" &&
   exec stdbuf -o0 wpa_supplicant -Dwext -i"$WIFI" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1
  ) | {
    read pid &&
      grep -m1 "pre-shared key may be incorrect" &&
      kill -s PIPE "$pid"
  }'

Таким образом, wpa_supplicantон будет запущен в том же $BASHPIDпроцессе, что и подоболочка, которую мы создаем с помощью exec.

Мы получаем pid через канал и запускаем killот имени root.

Обратите внимание, если вы готовы подождать немного дольше,

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 |
  grep -m1 "pre-shared key may be incorrect"

Автоматически уничтожил бы wpa_supplicantс помощью SIGPIPE (системой, так что проблем с разрешением не возникло)в следующий разон что-то записывает в этот канал после grepтого, как исчезает.

Некоторые реализации оболочки не будут дожидаться возврата sudoafter grep(оставляя ее работающей в фоновом режиме до получения сигнала SIGPIPE), и с помощью bash, вы также можете сделать это, используя grep ... <(sudo ...)синтаксис, где bashне ждет возврата sudoafter .grep

Подробнее наGrep медленно завершает работу после нахождения совпадения?

решение2

Подоболочка относится к команде оболочки, которая является потомком некоторой оболочки, например потомком bash -iинтерактивной оболочки входа, которая предлагает вам $приглашение. Вы неиметьдля запуска вашей команды в подоболочке - вы можете выбрать запуск ее как независимого процесса. Похоже, это может быть уместно, поскольку вы не хотите, чтобы ее stdout / stderr портили вид вашего индикатора выполнения, и поскольку вы не хотите, чтобы родительская оболочка сообщала или даже замечала смерть своего потомка.

Для этого существуют стандартные инструменты, такие какдемонизироватьи nohup. (См. такжемужчина страницы.) Возможно, вам лучше всего подойдетнет. Вот пример использования его для запуска тривиальной программы, которая делаетнетсоздать nohup.out:

$ nohup true 2>&1 > /dev/null &

Пусть ваша программа или скрипт-обертка для вашей программы запишет свой PID в /tmp/my.pid -- bash сделает это доступным как $$переменная. Затем процесс мониторинга с полосой прогресса может

$ kill `cat /tmp/my.pid`

когда ему больше не нужна эта программа для выполнения какой-либо дальнейшей обработки. В качестве альтернативы вы можете предпочесть дать своей программе имя killall.

решение3

Возможно, вы ищете это

#!/bin/bash
(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) &
wait $! 2>/dev/null

echo subshell done

здесь подоболочка помещается в фоновый режим, затем родительская оболочка ждет, но вывод ожидания отправляется в /dev/null. Это перехватывает сообщение Terminated.

Обратите внимание, если вы измените ожидание захвата выходных данных на файл, например, wait $! 2>wait_outputто вы увидите

 ./foo.sh: line 5:  1939 Terminated              ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )

показывая, что Terminatedэто из родительской оболочки.

Небольшая проверка, чтобы увидеть, что это работает, если есть какая-то активность перед тем, как kill будет завершен.

#!/bin/bash
(subshell=$BASHPID
 (sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output

echo subshell done

Этот пример остановится на секунду перед печатью subshell done. Этот пример также показывает, как перейти в фоновый режим и подождать все на одной строке, например & wait 2>wait_output. Я не уверен, есть ли у этого какие-либо преимущества/недостатки по сравнению с примером с wait $!.

Главное, что следует здесь отметить, это то, что Terminatedсообщения поступают из родительского элемента управления заданиями оболочки верхнего уровня. Это то, что видит завершение подоболочки и генерирует сообщение. Так что именно там вы хотите перехватить вывод. Перенаправление waitвывода команды делает это.

Связанный контент