Não tenho muita experiência em usar tee, então espero que isso não seja muito básico.
Depois de ver uma das respostas a essa pergunta, me deparei com um estranho comportamento tee
.
Para que eu produza a primeira linha e uma linha encontrada, eu 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 em que executei isso (em zsh), o resultado estava na ordem errada, os cabeçalhos das colunas estavam abaixo dos resultados do grep (no entanto, 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
Somente a primeira linha é impressa e nada mais! Posso usar tee para redirecionar para grep, ou estou fazendo isso da maneira errada?
Enquanto escrevia essa pergunta, o segundo comando realmente funcionou uma vez para mim, executei-o novamente cinco vezes e depois voltei ao resultado de uma linha. Este é apenas o meu sistema? (Estou executando o zsh no tmux).
Finalmente, por que com o primeiro comando "grep syslog" não é mostrado como resultado (existe apenas um resultado)?
Para 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 o cabeçote está causando o comando inteiro truncar (conforme 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
ps aux | sed -n -e '1p' -e '/syslog/p'
.Respostas:
Os comandos
grep
ehead
iniciam quase ao mesmo tempo e ambos recebem os mesmos dados de entrada como quiserem, mas geralmente à medida que os dados se tornam disponíveis. Existem algumas coisas que podem introduzir a saída 'não sincronizada' que inverte as linhas; por exemplo:Os dados multiplexados são
tee
realmente enviados para um processo antes do outro, dependendo principalmente da implementação detee
. Umatee
implementação simples teráread
alguma quantidade de entrada e, em seguidawrite
, duas vezes: uma vez para stdout e outra para seu argumento. Isso significa que um desses destinos receberá os dados primeiro.No entanto, todos os tubos 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 é necessário para a saída (ou seja, a
grep
linha de ped) antes que o outro comando (head
) tenha recebido qualquer dado em todos.Não obstante o acima exposto, também é possível que um desses comandos receba os dados, mas não consiga fazer nada com eles a tempo, e o outro comando receba mais dados e os processe rapidamente.
Por exemplo, mesmo se
head
egrep
são enviados os dados uma linha de cada vez, sehead
não sabe como lidar com ele (ou fica adiada por agendamento kernel),grep
pode mostrar seus resultados anteshead
mesmo tem a chance de. Para demonstrar, tente adicionar um atraso:ps aux | tee >(sleep 1; head -n1) | grep syslog
Isso quase certamente produzirá agrep
saída primeiro.Acredito que muitas vezes você só obtém uma linha aqui, porque
head
recebe a primeira linha de entrada e depois fecha seu stdin e sai. Quandotee
vê que seu stdout foi fechado, ele fecha seu próprio stdin (saída deps
) e sai. Isso pode depender da implementação.Efetivamente, os únicos dados
ps
a serem enviados são a primeira linha (definitivamente, porquehead
está controlando isso), e talvez algumas outras linhas anteshead
etee
fechem seus descritores stdin.A inconsistência em saber se a segunda linha aparece é introduzida por tempo:
head
fecha stdin, masps
ainda está enviando dados. Esses dois eventos não estão bem sincronizados, portanto a linha que contémsyslog
ainda tem chance de chegar aotee
argumento de (ogrep
comando). Isso é semelhante às explicações acima.Você pode evitar esse problema completamente usando comandos que aguardam todas as entradas antes de fechar stdin / exit. Por exemplo, use em
awk
vez dehead
, que lerá e processará todas as suas linhas (mesmo que não causem saída):Mas observe que as linhas ainda podem aparecer fora de ordem, como acima, o que pode ser demonstrado por:
Espero que não tenham sido muitos detalhes, mas há muitas coisas simultâneas interagindo entre si. Processos separados são executados simultaneamente sem nenhuma sincronização, portanto, suas ações em qualquer execução específica podem variar; às vezes, ajuda a aprofundar os processos subjacentes para explicar o porquê.
fonte
ps aux | tee >(grep syslog) | head -n1
Haveria uma maneira melhor de executar que pararia dehead
fechar o stdout. Uau, este comando já começou a dar saída agora, mas como aconteceria em linha com a sua resposta, parece ser truncadoUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
head
. Atualizei a resposta com este exemplo:ps aux | tee >(grep syslog) | awk 'NR == 1'
>(cmd)
, o shell cria um pipe nomeado e passa isso como argumento para o comando (tee
). Entãotee
está escrevendo para stdout (canalizado paraawk
) e também para esse argumento. É o mesmo quemkfifo a_fifo ; grep ... a_fifo
em um shell eps | tee a_fifo | awk ...
em outro.echo >(exit 0)
, o que ecoará o argumento real transmitido pelo shell (no meu caso, ele se torna/dev/fd/63
). Isso deve funcionar da mesma maneira no bash e no zsh.grep syslog
nem sempre é mostrado, pois depende do tempo. Ao usar o pipeline de shell, você está executando comandos quase simultaneamente. Mas a principal coisa aqui é a palavra "quase". Seps
concluir a verificação de todos os processos antes do grep ser lançado, ele não estará na lista. Você pode obter resultados aleatórios dependendo da carga do sistema etc.O mesmo acontece com o seu tee. É executado em segundo plano no subshell e pode ser disparado antes ou depois do grep. É por isso que a ordem de saída é inconsistente.
Quanto à questão do tee, seu comportamento é bastante estranho. Isso ocorre porque não é usado da maneira normal. Ele é executado sem argumentos, o que significa que ele deve apenas copiar dados do stdin para o stdout. Mas o stdout é redirecionado para subshell running head (no primeiro caso) ou grep (segundo caso). 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 subshell stdin. No zsh, ele é confiável da maneira que você deseja (imprimindo a primeira linha do ps e as linhas pesquisadas), cada vez que tento,
fonte
Um pouco hackish, mas aqui está a minha solução, na forma de uma
psgrep()
função shell que eu uso:Redirecione a
ps
linha do cabeçalho paraSTDERR
,grep
em seguidaSTDOUT
, remova, mas primeiro remova ogrep
próprio comando, para evitar a linha de "ruído" decorrente degrep
si mesma:fonte