Eu quero implementar algo assimPerguntas/Respostasmas 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 com que apenas subshell done
retorne 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 contextualizar. 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
Responder1
Observação:
wait $subshell
não funcionará porque$subshell
não é filho do processo que você está executandowait
. De qualquer forma, você não esperaria que o processo fizesse isso,wait
então não importa muito.kill $subshell
vai matar o subshell, mas nãosleep
se o subshell tiver conseguido iniciá-lo no momentokill
da execução. No entanto, você pode executarsleep
o mesmo processo comexec
- você pode usar SIGPIPE em vez de SIGTERM para evitar a mensagem
- deixar uma variável sem aspas em contextos de lista tem um significado muito especial em
bash
.
Então, tendo dito tudo isso, você pode fazer:
(
subshell=$BASHPID
kill -s PIPE "$subshell" &
sleep 600
)
echo subshell done
(substitua sleep 60
por exec sleep 60
se quiser kill
matar sleep
e não apenas o subshell, que neste caso pode nem ter tempo de ser executado sleep
no momento em que você o mata).
De qualquer forma, não tenho certeza do que você deseja alcançar com isso.
sleep 600 &
seria uma maneira mais confiável de iniciar sleep
em segundo plano se é isso que você deseja fazer (ou (sleep 600 &)
se deseja ocultar esse sleep
processo do shell principal)
Agora com o seu real
sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf
comando, observe que sudo
isso 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, será executado wpa_supplicant
no mesmo processo, portanto, no final, você terá três processos (além do restante do script) na wpa_supplicant
ascendência de:
- o subnível
- sudo quando criança de 1 ano
- wpa_supplicant (que anteriormente executava stdbuf) como filho de 2
Se você matar 1, isso não matará automaticamente 2. Se você matar 2, no entanto, a menos que seja com um sinal como SIGKILL que não pode ser interceptado, isso matará 3 ao sudo
encaminhar os sinais que recebe para o comando que executa .
De qualquer forma, esse não é o subshell que você gostaria de eliminar aqui, é 3 ou pelo menos 2.
Agora, se ele estiver rodando como root
e o resto do script não estiver, você não conseguirá eliminá-lo tão facilmente.
Você precisaria que kill
fosse feito como root
, então você precisaria de:
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"
}'
Dessa forma, wpa_supplicant
será executado no mesmo $BASHPID
processo do subshell que estamos fazendo com exec
.
Passamos o pid pelo cano e executamos kill
como root.
Observe que se você estiver pronto para esperar um pouco mais,
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"
Teria wpa_supplicant
matado automaticamente com um SIGPIPE (pelo sistema, portanto sem problema de permissão) opróxima vezele escreve algo naquele cano depois que grep
ele desaparece.
Algumas implementações de shell não esperariam pelo retorno sudo
do after grep
(deixando-o rodando em segundo plano até receber o SIGPIPEd) e, com bash
, você também pode fazer isso usando a grep ... <(sudo ...)
sintaxe, onde bash
não espera pelo retorno do sudo
after .grep
Mais emGrep lento para sair depois de encontrar a correspondência?
Responder2
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ãoterpara 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 até mesmo perceba a morte de seu filho.
Existem ferramentas padrão para fazer isso, comodemonizare não. (Veja tambémhomem Páginas.) Você pode estar melhor comnada. Aqui está um exemplo de como usá-lo para executar um programa trivial, que nãonãocrie nohup.out:
$ nohup true 2>&1 > /dev/null &
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 pode
$ kill `cat /tmp/my.pid`
quando não precisar mais desse programa para fazer mais processamento. Alternativamente, você pode preferir dar o nome do seu programa para killall
.
Responder3
Você pode estar procurando por isso
#!/bin/bash
(subshell=$BASHPID
(kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) &
wait $! 2>/dev/null
echo subshell done
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 capta a Terminated
mensagem.
Observe que se você alterar a espera para capturar a saída para o arquivo, por exemplo wait $! 2>wait_output
, você verá
./foo.sh: line 5: 1939 Terminated ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )
mostrando que Terminated
é do shell pai.
Uma pequena verificação para ver se funciona se houver alguma atividade antes de matar é
#!/bin/bash
(subshell=$BASHPID
(sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output
echo subshell done
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 em relação ao exemplo com wait $!
.
O principal a ser observado aqui é que as Terminated
mensagens vêm do controle de tarefa do shell pai de nível superior. É isso que vê o subshell terminar e gera a mensagem. Então é aí que você deseja capturar a saída. Redirecionar a wait
saída do comando faz isso.