Wie verwende ich sed, um kontinuierlich Streaming-Ausgaben zu manipulieren?

13

Ich stelle eine Präsentation für ein nicht-technisches Publikum zusammen. Ich habe ein Programm, das in Bash ausgeführt wird und einen kontinuierlichen Strom von Werten ausgibt, von denen einige wichtig sind. Ich möchte die wichtigen Ergebnisse hervorheben, die angezeigt werden, damit sich das Publikum einen Eindruck von ihrer Häufigkeit verschaffen kann. Das Problem ist, dass ich nicht sedin der Lage bin, mit einem laufenden Stream zu arbeiten. Es funktioniert gut, wenn ich die Ergebnisse in eine Datei lege, wie in:

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

Aber wenn ich das Gleiche auf der laufenden Ausgabe versuche, wie folgt:

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

sedtut nichts. Irgendwelche Gedanken?

Wie Lambert hilfreich genug war, um darauf hinzuweisen, war mein Sprichwort, seddas nichts bewirkt, vage. Was passiert ist, dass das Programm wie gewohnt auf ausgibt stdout(ich bin mir ziemlich sicher, dass es nicht schreibt stderr), auch wenn es durchgeleitet wird sed.

Das Problem scheint zu sein, dass der Befehl ein zweites Programm aufruft, das dann auf stdout ausgegeben wird. Das erste Programm druckt einige Zeilen. diese kann ich bearbeiten. Dann gibt es einen Strom von Werten, die vom zweiten Programm gedruckt werden. diese kann ich nicht bearbeiten.

Perl- und awk-Methoden funktionieren auch nicht.

P Jones
quelle
5
Tut stdbuf -o0 command | sed "s/some text/some text bolded/" Arbeit?
FloHimself
3
Mit "sed does nothing" meinen Sie, dass die Substitution nicht erfolgt ist oder Sie keine Ausgabe haben? Möglicherweise schreibt der Befehl an stderr statt an stdout? Wenn Sie etwas hervorheben möchten, das Sie verwenden könntencommand|egrep 'some text|$'
Lambert
Schreibt der Befehl nach stdout (und nicht nach stderr)?
Anthon,
1
Da der Text mehrmals vorkommt, sollten Sie eine g"globale" Ersetzung hinzufügen , da sonst nur das erste Vorkommen in einer Zeile ersetzt wird:sed "s/old/new/g"
ph0t0nix
@ ph0t0nix Nein, das ist nicht das Problem, aber es wäre ein Problem auf der ganzen Linie gewesen. Danke für den Tipp.
P Jones

Antworten:

12

Möglicherweise wird die Ausgabe des Befehls zwischengespeichert. Wenn der Befehl in ein Terminal schreibt, wird der Puffer in jeder neuen Zeile geleert, sodass er mit der erwarteten Rate angezeigt wird. Wenn der Befehl in eine Pipe schreibt, wird der Puffer nur dann geleert, wenn er einige Kilobyte erreicht, sodass er viel verzögert. Dies ist das Standardverhalten der Standard-Ein- / Ausgabebibliothek.

Um den Befehl zu zwingen, seine Ausgabe nicht zu bündeln, können Sie unbuffer(from expect) oder stdbuf(from GNU coreutils) verwenden.

unbuffer command | sed …
stdbuf -o0 command | sed …
Gilles 'SO - hör auf böse zu sein'
quelle
stdbufhat nicht funktioniert (es wurde bereits erwähnt, übrigens), aber unbufferhat !! Du hast keine Ahnung, wie glücklich du mich gemacht hast.
P Jones
Und danke für die prägnante Erklärung. Sehr hilfreich.
P Jones
sedselbst verwendet ein solcher Puffer, (cf ChennyStar post) , so dass die hier Beispiele kann nicht funktionieren , da seddas ist commandzu unbuffer: cat /etc/passwd | unbuffer sedaber sedselbst eine hat -uOption, so grepsuiteable in diesen Beispielen mehr sein könnte. Vielen Dank für Ihre Hintergrundinformationen! Gute Antwort!
Mathe
4

sed hat eine Option dafür:

-u, - ungepuffert

Dadurch werden minimale Datenmengen aus den Eingabedateien geladen und die Ausgabepuffer häufiger geleert. Sehen Sie man sedfür weitere Details.

ChennyStar
quelle
1
sedNur GNU (nicht BSD sed), und ich glaube, dies würde das Puffern des Befehls am Pipe-Start immer noch nicht verhindern. Aber gut zu erwähnen. :)
Wildcard
In der Manpage stdbuf heißt es: "Wenn COMMAND die Pufferung seiner Standard-Streams anpasst (z. B. 'tee'), werden die entsprechenden Änderungen durch 'stdbuf' überschrieben." Es sieht so aus, als würde sed in diese Kategorie fallen, aber das '-u'-Flag macht es in einer ungepufferten Pipe-Kette nützlich.
MattSmith
2

Ich würde awk benutzen

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

wo

  • /some important stuff/ wähle eine wichtige Zeile aus, wie in sed
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; rot drucken
    • benutze 32,33 für grün, gelb ...
    • Mit $ 1, $ 2 kann ein bestimmtes Feld ausgewählt werden
  • andere Zeilen werden nur so wie sie sind gedruckt

Der entscheidende Punkt ist, dass commanddie Linien bündig sein sollten, aber das sollte der Fall sein, wenn Sie viel Output haben.

Archemar
quelle
Hinweis: Die Frage besagt nicht, dass die gesamte Zeile "fett" sein sollte, sondern nur "etwas Text". Auch wenn es möglich ist, diese Funktion in awk(using sub()or gsub()) zu implementieren , ist im Falle dieser primitiven Substitution sedsicherlich das geeignete Werkzeug.
Janis
1

Die perl weg:

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

oder mit einer kontinuierlichen Ausgabe:

Ein Bash-Skript für die Ausgabe cont:

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

Testen Sie mit:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - Fett oder erhöhte Intensität
  • ${1} - das backreferenze
  • \x1b[0m - Alle Attribute zurücksetzen

Ausgabe:

Einige Sachen
Einige Sachen
Einige Sachen
Einige Sachen

Weitere Escape-Codes hier .

AB
quelle