Come usare sed per manipolare l'output in streaming continuo?


13

Sto mettendo insieme una presentazione per un pubblico non tecnico. Ho un programma in esecuzione in bash che genera un flusso continuo di valori, alcuni dei quali sono importanti. Vorrei evidenziare i risultati importanti quando vengono visualizzati in modo che il pubblico possa farsi un'idea della loro frequenza. Il problema è che non riesco seda operare su un flusso in esecuzione. Funziona bene se inserisco i risultati in un file, come in:

cat output.txt | sed "s/some text/some text bolded/"

Ma se provo la stessa cosa sull'output in esecuzione, in questo modo:

command | sed "s/some text/some text bolded/"

sednon fa nulla. qualche idea?

Come Lambert è stato abbastanza utile da sottolineare, il mio dire che sednon fa nulla era vago. Quello che sta succedendo è che il programma esce stdout(sono quasi sicuro che non stia scrivendo stderr) come farebbe normalmente, anche se è stato inviato sed.

Il problema sembra essere che il comando chiama un secondo programma, che quindi viene emesso su stdout. Ci sono alcune righe stampate dal primo programma; questi che posso modificare. Quindi c'è un flusso di valori stampati dal secondo programma; questi non posso modificarli.

I metodi Perl e Awk non funzionano neanche.


5
Funziona stdbuf -o0 command | sed "s/some text/some text bolded/"?
FloHimself

3
Con 'sed non fa nulla' intendi che la sostituzione non viene effettuata o non hai alcun output? Il comando potrebbe scrivere a stderr invece che a stdout? Se vuoi evidenziare qualcosa che potresti usarecommand|egrep 'some text|$'
Lambert

Il comando sta scrivendo su stdout (e non su stderr)?
Anthon,

1
Dato che il testo appare più di una volta dovresti aggiungere una gsostituzione "globale" ottenuta, altrimenti verrà sostituita solo la prima occorrenza su una riga:sed "s/old/new/g"
ph0t0nix

@ ph0t0nix No, non è questo il problema, ma sarebbe stato un problema lungo la linea. Grazie per il consiglio.
P Jones,

Risposte:


12

È probabile che l'output del comando sia bufferizzato. Quando il comando scrive su un terminale, il buffer viene scaricato su ogni nuova riga, quindi viene visualizzato alla velocità prevista. Quando il comando scrive su una pipe, il buffer viene scaricato solo quando raggiunge alcuni kilobyte, quindi è molto indietro. Quindi è il comportamento predefinito della libreria di input / output standard.

Per forzare il comando a non comprimerne l'output, è possibile utilizzare unbuffer(da aspettarsi) o stdbuf(da coreutils GNU).

unbuffer command | sed …
stdbuf -o0 command | sed …

stdbufnon ha funzionato (è stato menzionato in precedenza, BTW), ma unbufferha funzionato !! Non hai idea di quanto mi hai reso felice.
P Jones,

E grazie per la spiegazione concisa. Molto utile.
P Jones,

sedstesso utilizza un buffer di questo tipo, (vedi il post di ChennyStar), quindi gli esempi qui potrebbero non funzionare poiché non sedsono più commandnecessari: cat /etc/passwd | unbuffer sedma esso sedstesso ha -uun'opzione, quindi greppotrebbe essere più adatto in questi esempi. Grazie mille per le informazioni di base! Bella risposta!
matematica

4

sed ha un'opzione per questo:

-u, - senza buffer

Che carica quantità minime di dati dai file di input e svuota i buffer di output più spesso. Vedi man sedper maggiori dettagli.


1
sedSolo GNU (non BSD sed), e credo che questo non impedirebbe il buffering del comando all'inizio della pipe. Ma buono a dirlo. :)
Wildcard il

La manpage stdbuf afferma che "Se COMMAND regola il buffering dei suoi stream standard (ad esempio" tee "), questo sostituirà le modifiche corrispondenti di" stdbuf "." Sembra che sed possa rientrare in questa categoria, ma la bandiera '-u' lo renderà utile in una catena di tubi senza buffer.
MattSmith,

2

Vorrei usare awk

  command | awk '/some important stuff/ { printf "%c[31m%s%c[0m\n",27,$0,27 ; next }
  { print ; } '

dove

  • /some important stuff/ seleziona una linea importante, come in sed
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; stampa in rosso
    • usa 32,33 per verde, giallo ...
    • $ 1, $ 2, possono essere utilizzati per selezionare un campo specifico
  • l'altra riga viene appena stampata "così com'è"

il punto chiave è che commanddovrebbe svuotare le linee, ma dovrebbe essere così se si ha molto output.


Nota: la domanda non dice che l'intera riga dovrebbe essere "in grassetto", ma solo "del testo". Sebbene sia possibile implementare anche quella funzione in awk(usando sub()o gsub()), nel caso di questa sostituzione primitiva sedè sicuramente lo strumento appropriato.
Janis,

1

Il modo perl:

command | perl -pe 's/(stuff)/\x1b[1m$1\x1b[0m/g'

o con un'uscita continua:

Uno script bash per l'output cont:

#!/bin/bash
while [ true ]
do
   echo "Some stuff"
done

Prova con:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - grassetto o maggiore intensità
  • ${1} - le backreferenze
  • \x1b[0m - resetta tutti gli attributi

Produzione:

Alcune cose
Alcune cose
Alcune cose
Alcune cose

Altri codici di escape qui .

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.