Come posso usare tee per reindirizzare a grep


13

Non ho molta esperienza nell'uso di tee, quindi spero che questo non sia molto semplice.

Dopo aver visto una delle risposte a questa domanda, mi sono imbattuto in uno strano comportamento tee.

Per poter visualizzare la prima riga e una riga trovata, posso usare questo:

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

Tuttavia, la prima volta che ho eseguito questo (in zsh) il risultato era nell'ordine sbagliato, le intestazioni di colonna erano al di sotto dei risultati grep (questo non è accaduto ancora), quindi ho provato a scambiare i comandi:

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

Viene stampata solo la prima riga e nient'altro! Posso usare tee per reindirizzare a grep o lo sto facendo nel modo sbagliato?

Mentre stavo scrivendo questa domanda, il secondo comando ha funzionato una volta per me, l'ho eseguito di nuovo cinque volte e poi di nuovo al risultato di una riga. Questo è solo il mio sistema? (Sto eseguendo zsh all'interno di tmux).

Infine, perché con il primo comando "grep syslog" non viene mostrato come risultato (c'è solo un risultato)?

Per controllo ecco il grep senza il 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

Aggiornamento: sembra che head stia causando il troncamento dell'intero comando (come indicato nella risposta di seguito) il comando di seguito restituisce ora il seguente:

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

Non è una risposta diretta alla tua domanda, ma sarebbe molto più pulito fare qualcosa del genere ps aux | sed -n -e '1p' -e '/syslog/p'.
jw013,

Non ho mai nemmeno pensato a sed, penso che possa essere una risposta adatta alla domanda correlata qui, ma in realtà sto cercando informazioni sul comportamento incoerente di questi comandi!
Rqomey,

Risposte:


19
$ 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

I comandi grepe headiniziano all'incirca allo stesso tempo ed entrambi ricevono gli stessi dati di input a loro piacimento, ma in generale, quando i dati diventano disponibili. Ci sono alcune cose che possono introdurre l'output 'non sincronizzato' che inverte le linee; per esempio:

  1. I dati multiplexati vengono teeeffettivamente inviati a un processo prima dell'altro, a seconda principalmente dell'implementazione di tee. Una semplice teeimplementazione avrà una readcerta quantità di input, e poi writedue volte: una volta a stdout e una volta al suo argomento. Ciò significa che una di quelle destinazioni riceverà prima i dati.

    Tuttavia, i tubi sono tutti bufferizzati. È probabile che questi buffer siano 1 riga ciascuno, ma potrebbero essere più grandi, il che può far sì che uno dei comandi di ricezione veda tutto ciò che serve per l'output (es. La greplinea ped) prima che l'altro comando ( head) abbia ricevuto dati su tutti.

  2. Nonostante quanto sopra, è anche possibile che uno di questi comandi riceva i dati ma non sia in grado di farci nulla in tempo, quindi l'altro comando riceve più dati e li elabora rapidamente.

    Ad esempio, anche se heade grepvengono inviati i dati una riga alla volta, se headnon sa come gestirli (o viene ritardato dalla pianificazione del kernel), greppuò mostrare i suoi risultati prima headancora che ne abbia la possibilità. Per dimostrare, prova ad aggiungere un ritardo: ps aux | tee >(sleep 1; head -n1) | grep syslogquesto quasi sicuramente produrrà grepprima l' output.

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

Credo che spesso ottieni solo una riga qui, perché headriceve la prima riga di input e quindi chiude il suo stdin ed esce. Quando teevede che il suo stdout è stato chiuso, quindi chiude il proprio stdin (output da ps) ed esce. Questo potrebbe dipendere dall'implementazione.

In effetti, gli unici dati che pspossono essere inviati sono la prima riga (sicuramente, perché headsta controllando questo), e forse qualche altra riga prima heade teechiudi i loro descrittori di stdin.

L'incongruenza con la comparsa della seconda riga viene introdotta dai tempi: headchiude lo stdin, ma pscontinua a inviare dati. Questi due eventi non sono ben sincronizzati, quindi la riga che contiene syslogha ancora la possibilità di passare teeall'argomento (il grepcomando). Questo è simile alle spiegazioni sopra.

È possibile evitare del tutto questo problema utilizzando i comandi che attendono tutti gli input prima di chiudere stdin / exit. Ad esempio, utilizzare al awkposto di head, che leggerà ed elaborerà tutte le sue righe (anche se non causano output):

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

Ma nota che le linee possono ancora apparire fuori servizio, come sopra, che può essere dimostrato da:

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

Spero che non ci siano troppi dettagli, ma ci sono molte cose simultanee che interagiscono tra loro. I processi separati vengono eseguiti contemporaneamente senza alcuna sincronizzazione, quindi le loro azioni su una determinata esecuzione possono variare; a volte aiuta a scavare in profondità nei processi sottostanti per spiegare il perché.


1
Risposta eccellente! In realtà l'ho chiesto perché sono interessato ai processi sottostanti. Quando le cose sono incostanti, lo trovo interessante. Ci sarebbe un modo migliore di correre ps aux | tee >(grep syslog) | head -n1che smetterebbe di headchiudere stdout. Wow, questo comando ha iniziato a dare output adesso, ma come accadrebbe in linea con la tua risposta, sembra essere troncatoUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey,

1
Puoi usare qualcosa che non chiude stdin invece di head. Ho aggiornato la risposta con questo esempio:ps aux | tee >(grep syslog) | awk 'NR == 1'
mb

1
@KrzysztofAdamski, quando si utilizza >(cmd), la shell crea una pipe denominata e la passa come argomento al comando ( tee). Quindi teesta scrivendo su stdout (reindirizzato a awk) e anche su quell'argomento. È lo stesso mkfifo a_fifo ; grep ... a_fifodi una shell e ps | tee a_fifo | awk ...di un'altra.
sig.

1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/… - Try echo >(exit 0), che farà eco all'argomento reale passato dalla shell (nel mio caso, diventa /dev/fd/63). Questo dovrebbe funzionare allo stesso modo su bash e zsh.
MR

1
@mrb: è una funzionalità molto interessante che non conoscevo prima, grazie. Funziona in qualche modo strano in bash, tuttavia, vedi pastebin.com/xFgRcJdF . Purtroppo non ho tempo di indagare su questo adesso, ma lo farò domani.
Krzysztof Adamski,

2

grep syslognon viene sempre visualizzato in quanto dipende dai tempi. Quando si utilizza la pipeline della shell, si eseguono i comandi quasi contemporaneamente. Ma la cosa chiave qui è la parola "quasi". Se pstermina la scansione di tutti i processi prima dell'avvio di grep, non sarà nell'elenco. È possibile ottenere risultati casuali a seconda del carico del sistema, ecc.

Una cosa simile succede con la tua maglietta. Viene eseguito in background in subshell e può essere attivato prima o dopo grep. Questo è il motivo per cui l'ordine di output è incoerente.

Per quanto riguarda la domanda sul tee, il suo comportamento è piuttosto strano. Questo perché non viene utilizzato in modo normale. Viene eseguito senza alcun argomento, il che significa che dovrebbe semplicemente copiare i dati dal suo stdin allo stdout. Ma il suo stdout viene reindirizzato a subshell running head (nel primo caso) o grep (secondo caso). Ma viene anche reindirizzato al comando successivo. Penso che ciò che accade in questo caso dipenda effettivamente dall'implementazione. Ad esempio sulla mia bash 4.2.28, nulla viene mai scritto su subshell stdin. Su zsh, funziona in modo affidabile nel modo desiderato (stampando sia la prima riga di ps che le righe cercate), ogni volta che provo,


Ciò spiega comunque una cosa, sono sorpreso che i tee ritardino il funzionamento di grep in misura notevole!
Rqomey,

0

Un po 'hacker, ma ecco la mia soluzione, sotto forma di una psgrep()funzione shell che uso:

Reindirizzare la psriga di intestazione su STDERR, quindi grepsu STDOUT, ma prima rimuovere il grepcomando stesso, per evitare che la riga "noise" derivi da grepse stessa:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.