Я хочу реализовать что-то вроде этогоВопрос/Ответно для под-оболочки. Вот минимальный пример того, что я пытаюсь сделать:
(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
родословной:
- подоболочка
- sudo как потомок 1
- 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
того, как исчезает.
Некоторые реализации оболочки не будут дожидаться возврата sudo
after grep
(оставляя ее работающей в фоновом режиме до получения сигнала SIGPIPE), и с помощью bash
, вы также можете сделать это, используя grep ... <(sudo ...)
синтаксис, где bash
не ждет возврата sudo
after .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
вывода команды делает это.