Como canalizar um comando bash e manter o Ctrl + C funcionando?

3

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
    
por Adrian Maire 27.11.2017 / 09:24

2 respostas

8

Ctrl + C faz com que um SIGINT seja enviado para todos os processos no pipeline (já que todos eles são executados no mesmo grupo de processos que correspondem ao primeiro plano do seu shell interativo). / p>

Então, em:

loopHelloWorld | 
  while IFS= read -r line; do
    echo "$line"
  done

O processo que está executando loopHelloWorld e o que está executando o subshell que executa o loop while obterá o SIGINT .

Se loopHelloWorld gravar a mensagem Ctrl+C to nicely shutdown em sua stdout, ela também será gravada no pipe. Se isso é após , o subshell do outro lado já morreu, então loopHelloWorld irá também receber um SIGPIPE, o qual você precisaria manipular.

Aqui, você deve escrever a mensagem para stderr, já que não é a saída normal do seu comando (não se aplica ao exemplo ping ). Então não passaria pelo cano.

Ou você pode ter o subshell executando o loop while ignorando o SIGINT, então ele continua lendo a saída loopHelloWorld após o SIGINT:

loopHelloWorld | (
  trap '' INT
  while IFS= read -r line; do
    printf '%s\n' "$line"
  done
)

que, no entanto, fará com que o status de saída do pipeline seja 0 quando você pressionar Ctrl + C .

Outra opção para esse exemplo específico seria usar zsh ou ksh93 em vez de bash . Nesses shells, o loop while seria executado no processo principal do shell, portanto, não seria afetado pelo SIGINT.

Isso não ajudaria em loopHelloWorld | cat , onde cat e loopHelloWorld são executados no grupo de processos em primeiro plano.

    
por 27.11.2017 / 11:29
2

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
    
por 27.11.2017 / 11:11

Tags