Por que este one-liner falha em produzir saída? [duplicado]

5

O seguinte não produz nada no bash:

while true ; do upower -d ; sleep 1 ; done | grep percentage | uniq

Descobri que não importa qual seja o último ou o segundo até o último programa nessa cadeia. O penúltimo programa sempre produz a saída esperada, e o último sempre falha em produzir qualquer coisa.

Também não parece importar se eu envolver o loop while em um subshell (via ( while ... done ) ), ou envolver tudo menos o último comando em uma subshell e canalizar para o último comando.

Estou profundamente confuso com esse comportamento.

Meu instinto me diz ...

... que há algum tipo de travamento de E / S na cadeia criada pelo bloqueio de leituras e gravações e o fato de que o loop while não está sempre produzindo saída. Mas, por outro lado, já fiz esse tipo de coisa muitas vezes antes, sem nenhum problema. Além disso, se fosse assim, o problema não existiria entre o loop while e o próximo comando? Então estou perplexa. Fornecerá informações da versão bash se ninguém mais puder reproduzir.

Caso não seja imediatamente óbvio ...

O objetivo desta linha de código é imprimir todas as alterações na porcentagem de bateria, mas somente imprimir novos níveis de bateria. Usa polling. Eu suponho que eu poderia ter o mesmo comportamento simplesmente executando

upower --monitor-detail | grep percentage | uniq

Mas isso é uma coisa única, e eu não planejava gastar mais do que 5 segundos pensando nisso até que o acima começou a falhar. Nesse ponto, tornou-se um problema interessante. Além disso, eu não sei se o detalhe do monitor apenas faz polling sob o capô, de qualquer forma (e eu não estou correndo um strace para verificar).

EDIT: Aparentemente, a versão --monitor-detail acima também não produz nada (ou, pelo menos, parece. A frequência de sondagem / atualização é muito baixa, então eu poderia não esperei o tempo suficiente, embora eu saiba que esperei o tempo suficiente para a edição original). Agora estou muito confuso. Eu acho que eu deveria correr essa linha depois de tudo ...

    
por Parthian Shot 15.06.2015 / 04:31

3 respostas

9

Você deve usar grep --line-buffered percentage ou então levará um tempo muito longo para que o buffer grep stdout seja preenchido por sua saída.

    
por 15.06.2015 / 04:46
7

Além da resposta de Marco d'Itri, nem todas as ferramentas suportam o armazenamento em buffer personalizado. Caso a ferramenta que você está executando não tenha, ou caso você queira executá-la usando um tamanho de buffer personalizado, você pode usar stdbuf para substituir o comportamento de buffer da ferramenta; neste caso, por exemplo, para forçar a saída a ser armazenada em buffer:

while true ; do upower -d ; sleep 1 ; done | stdbuf -oL grep 'percentage' | uniq

Para forçar a saída a ser unbuffered:

while true ; do upower -d ; sleep 1 ; done | stdbuf -o0 grep 'percentage' | uniq

Para forçar a saída para buffer em blocos de 512 B:

while true ; do upower -d ; sleep 1 ; done | stdbuf -o512 grep 'percentage' | uniq

Você pode adicionar K, M, G, [...] para especificar a saída em KB, MB, GB, [...]:

while true ; do upower -d ; sleep 1 ; done | stdbuf -o512K grep 'percentage' | uniq
    
por 15.06.2015 / 05:13
2

Com qualquer POSIX sed , você pode obter a saída com buffer de linha com o comando w rite.

while  sleep 1
do     upower -d
done | 
sed -n /percentage/w\ /dev/fd/1 |
uniq

... funcionará em sistemas que suportam os /dev/fd/[num] links (como praticamente qualquer sistema linux).

Mas você poderia fazer:

while  sleep 1
do     upower -d
done | 
sed '/percentage/!d;H;x
     /^\(.*\)\n$/d;g'

O texto acima deve funcionar em vez disso - apenas agrupa as funções de uniq e grep em um único script sed .

    
por 15.06.2015 / 05:48