Cat trava ao tentar ler STDIN vazio

2

Meu script tenta coletar informações que podem ou não estar presentes em STDIN no momento da execução, mas o gato trava se o pipe estiver vazio. Como posso garantir que meu script pule esta etapa se não houver nada no STDIN?

stdin=$(cat <&0)

Note que especificamente eu não estou procurando nenhuma solução que se refira a / dev /, como eu pretendo que seja utilizável em um chroot se / dev / foi montado se possível.

    
por StarCrashr 27.09.2018 / 16:40

1 resposta

3

Normalmente, quando se trabalha com tubos e stdin, um tubo esgotado não tem significado especial. Novos dados ainda podem aparecer até que haja um eof que feche o canal. Seu cat termina em eof conforme o esperado. Se não houver dados antes de eof , só então você poderá dizer que o stdin estava realmente vazio.

Considere sender | receiver . Não é incomum que o sender seja (muito) mais lento que o receiver ; Nesse caso, o stdin de receiver é quase sempre esgotado, mas você dificilmente quer matar o cachimbo inteiro por causa disso. Portanto, as ferramentas que saem em stdin "vazio" (esgotado, mas ainda não terminado) são exceções em vez de padrão.

No Bash existe read -t 0 ( -t não é requerido pelo POSIX). De help read :

If TIMEOUT is 0, read returns immediately, without trying to read any data, returning success only if input is available on the specified file descriptor.

Por padrão, read lê stdin, então o status de saída de read -t 0 dirá se o stdin está "vazio". Mas cuidado! Um comando como

echo 1 | read -t 0

pode sair com êxito ou não, porque echo e read são executados simultaneamente, não sequencialmente. Para evitar isso, seu script deve sleep por um tempo antes de read -t 0 . Dependendo de onde o stdin vem, "um tempo" pode ser relativamente longo. Faça algo assim:

sleep 1
if read -t 0; then …   # process stdin here, you know it's non-empty

Você preenche uma variável com dados obtidos de stdin. Como armazenar dados binários em uma variável não é uma boa idéia (leia isso ), talvez seus dados sejam apenas texto. Em caso afirmativo, use read -t da seguinte forma:

read -r -t 5 -d $'
timeout --foreground 5 cat | wc -c
' stdin

Caractere nulo (que você não pode armazenar em uma variável do Bash) como um delimitador ( -d $'stdin' ) permitirá que você leia qualquer texto (por exemplo, com novas linhas) para a variável -t 5 . Depois de no máximo 5 segundos ( timeout ), o comando termina, permitindo que seu script continue.

Outra abordagem é com wc -c . Um exemplo básico do meu Debian:

{ timeout --foreground 5 dd bs=1 count=1 2>/dev/null && cat; } | wc -c

(Substitua cat pelo seu código que analisa stdin; é apenas um exemplo).

Isso deve lidar com dados binários muito bem. Se eof não obtiver wc , depois de 5 segundos ele será eliminado, portanto, eof obterá cat de qualquer maneira e a linha não será paralisada. O problema é que cat será eliminado, independentemente de estar processando algum dado no momento. Eu imagino que você quer obter todos os dados, se houver algum, mesmo que demore mais de 5 segundos. Versão melhorada:

echo 1 | read -t 0

Se o primeiro byte aparecer dentro de 5 segundos, eof será acionado. Em seguida, ele processará qualquer entrada adicional até wc , independentemente do tempo necessário. Tudo incluindo o primeiro byte (se houver) irá para eof . Se não houver um byte nem wc em 5 segundos, eof receberá apenas %code% ; a linha não fica parada.

    
por 27.09.2018 / 20:50