Como eu uso o tee para redirecionar para o grep?

10

Eu não tenho muita experiência em usar tee, então espero que isso não seja muito básico.

Depois de visualizar uma das respostas para esta questão me deparei com um estranho beheviour com tee .

Para que eu possa gerar a primeira linha e uma linha encontrada, posso usar isso:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

No entanto, na primeira vez que executei isso (em zsh) o resultado estava na ordem errada, os cabeçalhos das colunas estavam abaixo dos resultados do grep (isso não aconteceu novamente), então tentei trocar os comandos:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Apenas a primeira linha é impressa e nada mais! Posso usar o tee para redirecionar para o grep, ou estou fazendo isso da maneira errada?

Como eu estava digitando essa pergunta, o segundo comando realmente funcionou uma vez para mim, eu o executei novamente cinco vezes e depois retornei ao resultado de uma linha. Isso é apenas o meu sistema? (Eu estou rodando zsh dentro do tmux).

Finalmente, por que com o primeiro comando é "grep syslog" não mostrado como resultado (há apenas um resultado)?

Para o controle aqui está o grep sem o tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

Atualização: Parece que head está fazendo com que todo o comando trunque (como indicado na resposta abaixo) o comando abaixo agora está retornando o seguinte:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806
    
por Rqomey 12.09.2012 / 14:45

3 respostas

16
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Os comandos grep e head começam aproximadamente ao mesmo tempo, e ambos recebem os mesmos dados de entrada quando bem entenderem, mas geralmente, conforme os dados se tornam disponíveis. Existem algumas coisas que podem introduzir a saída 'não sincronizada' que inverte linhas; por exemplo:

  1. Os dados multiplexados de tee , na verdade, são enviados para um processo antes do outro, dependendo principalmente da implementação de tee . Uma implementação tee simples irá read alguma quantidade de entrada e, em seguida, write it duas vezes: uma vez para stdout e uma vez para seu argumento. Isso significa que um desses destinos receberá os dados primeiro.

    No entanto, todos os pipes são armazenados em buffer. É provável que esses buffers tenham 1 linha cada, mas podem ser maiores, o que pode fazer com que um dos comandos de recebimento veja tudo o que precisa para a saída (ou seja, a linha grep ped) antes do outro comando ( head ) recebeu todos os dados.

  2. Não obstante o acima, também é possível que um desses comandos receba os dados, mas não possa fazer nada com eles a tempo, e o outro comando receba mais dados e os processe rapidamente.

    Por exemplo, mesmo que head e grep enviem os dados uma linha por vez, se head não souber como lidar com isso (ou for atrasado pelo agendamento do kernel), grep pode mostrar seus resultados antes mesmo de head ter uma chance. Para demonstrar, tente adicionar um atraso: ps aux | tee >(sleep 1; head -n1) | grep syslog Isso quase certamente gerará a saída grep primeiro.

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Acredito que muitas vezes você só recebe uma linha aqui, porque head recebe a primeira linha de entrada e depois fecha seu stdin e sai. Quando tee vê que sua stdout foi fechada, ela fecha seu próprio stdin (saída de ps ) e sai. Isso pode depender da implementação.

Efetivamente, os únicos dados que ps consegue enviar é a primeira linha (definitivamente, porque head está controlando isso), e talvez algumas outras linhas antes de head & tee fecha seus descritores stdin.

A inconsistência de se a segunda linha aparece é introduzida pelo tempo: head fecha stdin, mas ps ainda está enviando dados. Esses dois eventos não são bem sincronizados, portanto, a linha contendo syslog ainda tem chance de chegar ao argumento tee (o comando grep ). Isso é semelhante às explicações acima.

Você pode evitar completamente esse problema usando comandos que aguardam todas as entradas antes de fechar stdin / sair. Por exemplo, use awk em vez de head , que lerá e processará todas as suas linhas (mesmo que não causem saída):

ps aux | tee >(grep syslog) | awk 'NR == 1'

Mas observe que as linhas ainda podem aparecer fora de ordem, como acima, o que pode ser demonstrado por:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

Espero que isso não seja muito detalhado, mas há muitas coisas simultâneas interagindo umas com as outras. Processos separados são executados simultaneamente sem qualquer sincronização, portanto, suas ações em qualquer execução específica podem variar; às vezes ajuda a cavar fundo nos processos subjacentes para explicar por quê.

    
por 12.09.2012 / 15:45
2

grep syslog nem sempre é exibido, pois depende do tempo. Ao usar o pipeline de shell, você está executando comandos quase simultaneamente. Mas a coisa chave aqui é a palavra "quase". Se ps terminar de varrer todos os processos antes que o grep seja iniciado, ele não estará na lista. Você pode obter resultados aleatórios, dependendo da carga do sistema, etc.

Coisa semelhante acontece com o seu tee. É executado em segundo plano em subshell e pode ser disparado antes ou depois do grep. É por isso que a ordem de saída é inconsistente.

Quanto à questão tee, o comportamento é bem estranho. Isso é porque não é usado normalmente. Ele é executado sem nenhum argumento, o que significa que ele deve apenas copiar os dados do stdin para o stdout. Mas o stdout é redirecionado para subshell running head (no primeiro caso) ou grep (2nd case). Mas também é canalizado para o próximo comando. Eu acho que o que acontece neste caso é realmente dependente da implementação. Por exemplo, no meu bash 4.2.28, nada é escrito para subdividir stdin. No zsh, ele funciona da maneira que você gostaria (imprimindo a primeira linha do ps e as linhas pesquisadas), cada vez que eu tento,

    
por 12.09.2012 / 15:25
0

Um pouco hackish, mas aqui está a minha solução, na forma de uma função psgrep() shell que uso:

Redirecione a linha de cabeçalho ps para STDERR e, em seguida, grep sobre STDOUT , mas primeiro remova o comando grep para evitar a linha "ruído" proveniente de grep :

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
    
por 02.08.2013 / 14:27