Eu quero implementar algo como este Q/A , mas para um sub-shell. Aqui está um exemplo mínimo do que estou tentando:
(subshell=$BASHPID
(kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)
echo subshell done
Como posso fazer isso apenas subshell done
retorna em vez de:
./test.sh: line 4: 5439 Terminated ( subshell=$BASHPID; ( kill $subshell && wait $subshell 2> /dev/null ) & sleep 600 )
subshell done
Editar: posso estar errado na terminologia aqui, por subshell, quero dizer o processo dentro do primeiro conjunto de colchetes.
Atualizar:
Quero postar o trecho do programa real para contexto, acima está uma simplificação:
# 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
Observação:
wait $subshell
não funcionará, pois$subshell
não é filho do processowait
em que você está executando. De qualquer forma, você não esperaria o processo fazerwait
isso, então não importa muito.kill $subshell
vai matar o subshell, mas nãosleep
se o subshell conseguiu iniciá-lo no momento dakill
execução. No entanto, você pode executarsleep
o mesmo processo comexec
bash
.Então, tendo dito tudo isso, você pode fazer:
(substitua
sleep 60
porexec sleep 60
se quiserkill
matarsleep
e não apenas o subshell, que neste caso pode nem ter tempo de ser executadosleep
no momento em que você o matar).De qualquer forma, não tenho certeza do que você deseja alcançar com isso.
seria uma maneira mais confiável de iniciar
sleep
em segundo plano se fosse isso que você queria fazer (ou(sleep 600 &)
se quisesse ocultar essesleep
processo do shell principal)Agora com o seu real
comando, observe que
sudo
gera um processo filho para executar o comando (apenas porque pode ser necessário registrar seu status ou executar algumas tarefas de sessão PAM posteriormente).stdbuf
no entanto, executaráwpa_supplicant
no mesmo processo, portanto, no final, você terá três processos (além do restante do script) nawpa_supplicant
ancestralidade de:Se você matar 1, isso não mata 2 automaticamente. Se você matar 2, no entanto, a menos que seja com um sinal como SIGKILL que não pode ser interceptado, isso matará 3 como
sudo
acontece ao encaminhar os sinais que recebe para o comando que executa .De qualquer forma, esse não é o subshell que você gostaria de matar aqui, são 3 ou pelo menos 2.
Agora, se estiver executando como
root
e o restante do script não, você não poderá eliminá-lo tão facilmente.Você precisaria que o
kill
fosse feito comoroot
, então você precisaria:Dessa forma,
wpa_supplicant
estará rodando no mesmo$BASHPID
processo que o subshell que estamos fazendo comexec
.Pegamos o pid pelo pipe e rodamos
kill
como root.Observe que, se você estiver pronto para esperar um pouco mais,
Teria
wpa_supplicant
matado automaticamente com um SIGPIPE (pelo sistema, portanto, sem problema de permissão) na próxima vez que gravar algo naquele canal depois quegrep
ele se for.Algumas implementações de shell não esperariam pelo retorno de
sudo
depoisgrep
(deixando-o rodando em segundo plano até obter SIGPIPEd), e combash
, você também pode fazer isso usando agrep ... <(sudo ...)
sintaxe, ondebash
não espera porsudo
nenhum dos dois retornosgrep
.Mais em Grep lento para sair depois de encontrar correspondência?
Subshell refere-se a um comando shell que é filho de algum shell, como filho do
bash -i
shell de login interativo que oferece um$
prompt. Você não precisa executar seu comando em um subshell - você pode optar por executá-lo como um processo independente. Parece que isso pode ser apropriado porque você não quer que seu stdout / stderr atrapalhe a aparência de sua barra de progresso e porque você não quer que o shell pai relate ou mesmo perceba a morte de seu filho.Existem ferramentas padrão para realizar isso, como daemonize e nohup. (Consulte também as páginas de manual .) Você pode se sair melhor com nohup . Aqui está um exemplo de como usá-lo para executar um programa trivial, que não cria nohup.out:
Faça com que seu programa, ou um script wrapper para seu programa, registre seu PID em /tmp/my.pid -- o bash disponibiliza isso como
$$
variável. Então o processo de monitoramento com a barra de progresso podequando não precisar mais desse programa para fazer mais processamento. Alternativamente, você pode preferir dar o nome do seu programa para
killall
.Você pode estar procurando por isso
aqui o subshell é colocado em segundo plano, então o shell pai espera, mas com a saída de espera enviada para /dev/null. Isso captura a
Terminated
mensagem.Observe que, se você alterar a espera para capturar a saída para o arquivo, por exemplo
wait $! 2>wait_output
, verámostrando que
Terminated
é do shell pai.Uma pequena verificação para ver se funciona se houver alguma atividade antes de matar é
Este exemplo fará uma pausa por um segundo antes de imprimir
subshell done
. Este exemplo também mostra como colocar em segundo plano e esperar tudo na mesma linha, por exemplo& wait 2>wait_output
. Não tenho certeza se isso tem alguma vantagem/desvantagem sobre o exemplo comwait $!
.O principal a observar aqui é que as
Terminated
mensagens vêm do controle de trabalho do shell pai de nível superior. Isso é o que vê o subshell terminar e gera a mensagem. Então é aí que você deseja capturar a saída. Redirecionar await
saída do comando faz isso.