Silenciosamente sub-submarino?

2

Eu quero implementar algo assim P / R , 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 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

Edit: Eu posso estar errado na terminologia aqui, por subshell quero dizer o processo dentro do primeiro conjunto de colchetes.

Atualização:

Eu quero postar o trecho do programa para o contexto, acima é 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
    
por Philip Kirkbride 24.11.2017 / 01:20

3 respostas

5

Nota:

  • wait $subshell não funcionará, pois $subshell não é filho do processo em que você está executando wait in. De qualquer forma, você não está esperando o processo fazer o wait , por isso não importa muito.
  • kill $subshell vai matar o subshell, mas não sleep se o subshell tiver conseguido iniciá-lo no tempo em que kill foi executado. No entanto, você pode executar sleep no mesmo processo com exec
  • você pode usar o SIGPIPE em vez do SIGTERM para evitar a mensagem
  • deixar uma variável sem aspas nos 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 você quiser que o kill mate sleep e não apenas o subshell, que neste caso pode nem ter tempo de executar sleep no momento em que você o mata ).

De qualquer forma, não tenho certeza do que você quer alcançar com isso.

sleep 600 &

seria uma maneira mais confiável de iniciar sleep em segundo plano se era isso que você queria fazer (ou (sleep 600 &) se quisesse ocultar esse processo sleep 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 gera um processo filho para executar o comando (somente porque pode ser necessário registrar seu status ou executar algumas tarefas de sessão do PAM posteriormente). No entanto, stdbuf executará wpa_supplicant no mesmo processo, então, no final, você terá três processos (além do restante do script) na ascendência de wpa_supplicant :

  1. o subshell
  2. sudo como filho de 1
  3. wpa_supplicant (que estava executando stdbuf anteriormente) como filho de 2

Se você matar 1, isso não mata 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, pois sudo acontece para encaminhar os sinais recebe o comando que ele executa.

De qualquer forma, essa não é a subcala que você gostaria de matar aqui, é 3 ou pelo menos 2.

Agora, se ele estiver sendo executado como root e o restante do script não for, você não poderá eliminá-lo tão facilmente.

Você precisaria que o kill fosse feito como root , então seria necessário:

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 estará sendo executado no mesmo processo $BASHPID do subshell que estamos fazendo com exec .

Recebemos o pid através do pipe 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 morto automaticamente com um SIGPIPE (pelo sistema, então não há problema de permissão) o da próxima vez ele grava algo naquele pipe depois que grep se foi.

Algumas implementações do shell não esperariam por sudo depois que grep retornou (deixando em execução em segundo plano até obter SIGPIPEd) e com bash , você também pode fazer isso usando a sintaxe grep ... <(sudo ...) , bash não aguarda sudo depois de grep ter retornado.

Mais em Grep lento para sair depois de encontrar correspondência?

    
por 26.11.2017 / 18:19
2

Subshell refere-se a um comando shell que é filho de algum shell, como o filho do shell de login interativo bash -i que oferece um prompt $ . Você não tem para 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 sua stdout / stderr atrapalhe a aparência da 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 isso, como daemonize e nohup. (Veja também man páginas .) Você pode ser o melhor com nohup . Aqui está um exemplo de como usá-lo para executar um programa trivial, que não cria nohup.out:

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

Tenha seu programa, ou um script de wrapper para seu programa, registre seu PID em /tmp/my.pid - bash o disponibiliza como a 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 processar mais nada. Alternativamente, você pode preferir dar o nome do seu programa para killall .

    
por 26.11.2017 / 18:18
1

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 aguarda mas espera a saída enviada para / dev / null. Isso captura a mensagem Terminated .

Observe que, se você alterar a espera para capturar a saída para o arquivo, por exemplo, wait $! 2>wait_output , então 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.

Um pequeno cheque 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 contextualizar e esperar tudo na mesma linha, por ex. %código%. Não tenho certeza se isso tem alguma vantagem / desvantagem em relação ao exemplo com & wait 2>wait_output .

A principal coisa a ser observada aqui é que as mensagens wait $! vêm do controle de tarefa do shell pai de nível superior. Isso é o que vê o subshell terminar e gera a mensagem. Então é aí que você quer pegar a saída. Redirecionar a saída do comando Terminated faz isso.

    
por 27.11.2017 / 00:36