Matar silenciosamente o subshell?

Matar silenciosamente o subshell?

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 doneretorne 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 $subshellnão funcionará porque $subshellnão é filho do processo que você está executando wait. De qualquer forma, você não esperaria que o processo fizesse isso, waitentão não importa muito.
  • kill $subshellvai matar o subshell, mas não sleepse o subshell tiver conseguido iniciá-lo no momento killda execução. No entanto, você pode executar sleepo 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 60por exec sleep 60se quiser killmatar sleepe não apenas o subshell, que neste caso pode nem ter tempo de ser executado sleepno 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 sleepem segundo plano se é isso que você deseja fazer (ou (sleep 600 &)se deseja ocultar esse sleepprocesso 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 sudoisso 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). stdbufno entanto, será executado wpa_supplicantno mesmo processo, portanto, no final, você terá três processos (além do restante do script) na wpa_supplicantascendência de:

  1. o subnível
  2. sudo quando criança de 1 ano
  3. 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 sudoencaminhar 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 roote o resto do script não estiver, você não conseguirá eliminá-lo tão facilmente.

Você precisaria que killfosse 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_supplicantserá executado no mesmo $BASHPIDprocesso do subshell que estamos fazendo com exec.

Passamos o pid pelo cano e executamos killcomo 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_supplicantmatado automaticamente com um SIGPIPE (pelo sistema, portanto sem problema de permissão) opróxima vezele escreve algo naquele cano depois que grepele desaparece.

Algumas implementações de shell não esperariam pelo retorno sudodo 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 bashnão espera pelo retorno do sudoafter .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 -ishell 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 Terminatedmensagem.

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 Terminatedmensagens 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 waitsaída do comando faz isso.

informação relacionada