Considerando um software de linha de comando personalizado (por exemplo, loopHelloWorld) que detecta ctrl+C para um bom desligamento. Como canalizar a resposta sem perder o Ctrl+C
?
$ loopHelloWorld
- Ctrl+C to nicely shutdown
Mas com o pipe, o pipe mata o software sem um bom desligamento
$ loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
Exemplo
ping example.com |
while IFS= read -r line; do
echo "$line"
done
Responder1
Ctrl+Cfaz com que um SIGINT seja enviado para todos os processos no pipeline (pois todos são executados no mesmo grupo de processos que corresponde ao trabalho em primeiro plano do seu shell interativo).
Então em:
loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
Tanto o processo em execução loopHelloWorld
quanto aquele que executa o subshell que executa o while
loop obterão o arquivo SIGINT
.
Se loopHelloWorld
escrever a Ctrl+C to nicely shutdown
mensagem em seu stdout, ela também será gravada no pipe. Se isso fordepoiso subshell na outra extremidade já morreu, então loopHelloWorld
irátambémreceba um SIGPIPE, com o qual você precisará lidar.
Aqui, você deve escrever essa mensagem em stderr, pois não é a saída normal do seu comando (embora não se aplique ao ping
exemplo). Então não passaria pelo cano.
Ou você pode fazer com que o subshell executando o loop while ignore o SIGINT para que continue lendo a loopHelloWorld
saída após o SIGINT:
loopHelloWorld | (
trap '' INT
while IFS= read -r line; do
printf '%s\n' "$line"
done
)
no entanto, isso faria com que o status de saída do pipeline fosse 0 quando você pressionasse Ctrl+C.
Outra opção para esse exemplo específico seria usar zsh
ou ksh93
em vez de bash
. Nesses shells, o while
loop seria executado no processo principal do shell, portanto não seria afetado pelo SIGINT.
Isso não ajudaria loopHelloWorld | cat
onde cat
e loopHelloWorld
executar no grupo de processos em primeiro plano.
Responder2
Use uma armadilha:
#! /bin/bash
trap handleInt SIGINT
interrupted=0
function handleInt {
echo "Interrupted..."
interrupted=1
}
while true
do
[[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
echo "Sleeping..."
sleep 1
read -r line
echo "$line"
done