Como verificar se um pipe está vazio e executar um comando nos dados, se não estiver?

31

Eu canalizei uma linha no script bash e quero verificar se o pipe tem dados antes de alimentá-lo com um programa.

Pesquisando achei sobre test -t 0 , mas não funciona aqui. Sempre retorna falso. Então, como ter certeza de que o pipe tem dados?

Exemplo:

echo "string" | [ -t 0 ] && echo "empty" || echo "fill"

Saída: fill

echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"

Saída: fill

Ao contrário de Padrão / maneira canônica de testar se precedendo saída produzida pipeline? a entrada precisa ser preservada para passá-lo para o programa. Isso generaliza que se concentra em enviar e-mail.

    
por zetah 29.02.2012 / 10:41

6 respostas

30

Não há como dar uma olhada no conteúdo de um pipe, nem há uma maneira de ler um caractere no pipe e depois colocá-lo de volta. A única maneira de saber que um pipe tem dados é ler um byte e, em seguida, você precisa obter esse byte para o seu destino.

Então faça exatamente isso: leia um byte; se você detectar um fim de arquivo, faça o que deseja fazer quando a entrada estiver vazia; se você ler um byte, em seguida, bifurque o que você deseja fazer quando a entrada não estiver vazia, insira o byte nela e canalize o restante dos dados.

first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n)
if [ -z "$first_byte" ]; then
  # stuff to do if the input is empty
else
  {
    printf "\${first_byte# }"
    cat
  } | {
    # stuff to do if the input is not empty
  }      
fi

O utilitário ifne de moreutils de Joey Hess executa um comando se sua entrada não estiver vazia. Geralmente não é instalado por padrão, mas deve estar disponível ou ser fácil de construir na maioria das variantes unix. Se a entrada estiver vazia, ifne não fará nada e retornará o status 0, que não pode ser distinguido do comando executado com êxito. Se você quiser fazer alguma coisa se a entrada estiver vazia, você precisa organizar o comando para não retornar 0, o que pode ser feito fazendo com que o caso de sucesso retorne um status de erro distinto:

ifne sh -c 'do_stuff_with_input && exit 255'
case $? in
  0) echo empty;;
  255) echo success;;
  *) echo failure;;
esac

test -t 0 não tem nada a ver com isso; testa se a entrada padrão é um terminal. Ele não diz nada de uma maneira ou outra sobre se alguma entrada está disponível.

    
por 29.02.2012 / 12:21
9

Uma solução simples é usar o comando ifne (se a entrada não estiver vazia). Em algumas distribuições, não é instalado por padrão. É uma parte do pacote moreutils na maioria das distros.

ifne executa um determinado comando se e somente se a entrada padrão não estiver vazia

Observe que, se a entrada padrão não estiver vazia, ela será transmitida por ifne para o comando especificado

    
por 21.07.2016 / 00:27
4

Você também pode usar test -s /dev/stdin (em um subnível explícito).

# test if a pipe is empty or not
echo "string" | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

echo "string" | tail -n+2 | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

: | (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')
    
por 14.10.2014 / 11:04
3

Pergunta antiga, mas no caso de alguém se deparar com ela como eu fiz: Minha solução é ler com um tempo limite.

while read -t 5 line; do
    echo "$line"
done

Se stdin estiver vazio, isso retornará após 5 segundos. Caso contrário, ele lerá todas as entradas e você poderá processá-las conforme necessário.

    
por 15.01.2016 / 05:41
3

verifique se o descritor de arquivo de stdin (0) está aberto ou fechado:

[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"
    
por 26.08.2017 / 13:13
0

Esta parece ser uma implementação ifne razoável no bash se você estiver bem lendo toda a primeira linha

ifne () {
        read line || return 1
        (echo "$line"; cat) | eval "$@"
}


echo hi | ifne xargs echo hi =
cat /dev/null | ifne xargs echo should not echo
    
por 23.09.2014 / 05:10

Tags