Eu canalizei uma linha no script bash e quero verificar se o pipe possui dados antes de enviá-lo para um programa.
Pesquisando eu encontrei, test -t 0
mas não funciona aqui. Sempre retorna falso. Então, como ter certeza de que o tubo possui 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 da maneira canônica / padrão de testar se o pipeline anterior produziu saída? a entrada precisa ser preservada para transmiti-la ao programa. Isso generaliza Como canalizar a saída de um processo para outro, mas apenas executar se o primeiro tiver saída? que se concentra no envio de e-mail.
Respostas:
Não há como espiar o conteúdo de um canal usando utilitários shell geralmente disponíveis, nem existe uma maneira de ler um caractere no canal e colocá-lo de volta. A única maneira de saber que um canal tem dados é ler um byte e, em seguida, você deve levá-lo ao seu destino.
Faça exatamente isso: leia um byte; se você detectar um final de arquivo, faça o que deseja fazer quando a entrada estiver vazia; se você ler um byte, bifurque o que você quer fazer quando a entrada não estiver vazia, canalize esse byte nele e canalize o restante dos dados.
O
ifne
utilitário do 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 fácil de construir na maioria das variantes unix. Se a entrada estiver vazia,ifne
nada fará e retornará o status 0, que não pode ser diferenciado do comando em execução com sucesso. Se você desejar fazer algo se a entrada estiver vazia, será necessário organizar o comando para não retornar 0, o que pode ser feito com o caso de sucesso retornando um status de erro distinto:test -t 0
não tem nada a ver com isso; ele testa se a entrada padrão é um terminal. Não diz nada de uma maneira ou de outra sobre se alguma entrada está disponível.fonte
peek
que possa retornar os dados reais de um pipe, e não apenas quanto dele existe. (em 4.4 tubos BSD, 386BSD etc foram implementados como pares de soquetes , mas isso foi destruído em versões posteriores do * BSD - embora eles os mantivessem bidirecionais).read -t 0
(t nesse caso significa tempo limite, se você se perguntar).Uma solução simples é usar o
ifne
comando (se a entrada não estiver vazia). Em algumas distribuições, ele não é instalado por padrão. É uma parte do pacotemoreutils
na maioria das distros.ifne
executa um determinado comando se e somente se a entrada padrão não estiver vaziaObserve que, se a entrada padrão não estiver vazia, ela será passada
ifne
para o comando especificadofonte
Pergunta antiga, mas no caso de alguém se deparar com isso, como eu fiz: minha solução é ler com um tempo limite.
Se
stdin
estiver vazio, isso retornará após 5 segundos. Caso contrário, ele lerá toda a entrada e você poderá processá-la conforme necessário.fonte
-t
infelizmente não faz parte do POSIX: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlverifique se o descritor de arquivo stdin (0) está aberto ou fechado:
fonte
[ -t 0 ]
verifica se fd 0 está aberto para um tty , não se está fechado ou aberto../that_script </dev/null
=> "stdin tem dados". Ou./that_script <&-
para ter o stdin realmente fechado .Você pode usar
test -s /dev/stdin
(em um subshell explícito) também.fonte
Na festança:
Detecta se uma entrada possui dados (sem ler nada). Então você pode ler a entrada (se a entrada estiver disponível no momento em que a leitura é executada):
Nota: Entenda que isso depende do tempo. Isso detecta se a entrada já possui dados apenas no momento da
read -t
execução.Por exemplo, com
a saída é
1
(falha na leitura, ou seja: entrada vazia). O eco grava alguns dados, mas não é muito rápido para iniciar e gravar seu primeiro byte, portanto,read -t 0
relatará que sua entrada está vazia, pois o programa ainda não escreveu nada.fonte
bash
ele fará umselect()
ou umioctl(FIONREAD)
deles, ou nenhum deles, mas não ambos, como deve funcionar.read -t0
está quebrado. Não o useUma maneira fácil de verificar se há dados disponíveis para leitura no Unix é com o
FIONREAD
ioctl.Não consigo pensar em nenhum utilitário padrão fazendo exatamente isso, então aqui está um programa trivial (melhor do que o
ifne
fromutils IMHO ;-)).Se não houver dados disponíveis no stdin, ele sairá com o status 1. Se houver dados, ele será executado
prog
. Se nãoprog
for fornecido, ele sairá com o status 0.Você pode remover a
poll
chamada se estiver interessado apenas nos dados disponíveis imediatamente . Isso deve funcionar com a maioria dos tipos de fds, não apenas com pipes.fionread.c
fonte
POLLHUP
evento também para lidar com o caso vazio? Isso funciona se houver várias descrições de arquivo na outra extremidade do canal?Se você gosta de frases curtas e enigmáticas:
Eu usei os exemplos da pergunta original. Se você não deseja que os dados canalizados usem a
-q
opção grepfonte
Esta parece ser uma implementação ifne razoável no bash, se você estiver de acordo em ler a primeira linha inteira
fonte
read
também retornará false se a entrada não estiver vazia, mas não contiver caracteres de nova linha, realizarread
algum processamento em sua entrada e poderá ler mais de uma linha, a menos que você a chame comoIFS= read -r line
.echo
não pode ser usado para dados arbitrários.Isso funciona para mim usando
read -rt 0
exemplo da pergunta original, sem dados:
fonte
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(falso negativo) etrue | { sleep .1; read -rt0 && echo YES; }
(falso positivo). Na verdade, a festança daread
será enganado até mesmo por fds abertas em somente gravação modo:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. A única coisa que parece fazer é umselect(2)
nesse fd.select
retornará um fd como "pronto" se umread(2)
bloco não bloquear, não importa se retornaráEOF
ou ocorrerá um erro. Conclusão:read -t0
é quebrado embash
. Não use.{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
não é um falso negativo porque (no momento em que a leitura é executada) não há entrada. Mais tarde (suspensão .1), essa entrada está disponível (para o gato).echo "" | { read -t0 && echo YES; }
imprime SIM, masecho "" | { read -rt0 && echo YES; }
não.