Jak używać sed do manipulowania ciągłym przesyłaniem strumieniowym?

13

Przygotowuję prezentację dla nietechnicznych odbiorców. Mam program działający w trybie bash, który generuje ciągły strumień wartości, z których kilka jest ważnych. Chciałbym podkreślić ważne wyniki podczas ich wyświetlania, aby publiczność mogła dowiedzieć się o ich częstotliwości. Problem polega na tym, że nie mogę seduruchomić działającego strumienia. Działa dobrze, jeśli umieszczę wyniki w pliku, jak w:

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

Ale jeśli spróbuję tego samego na uruchomionym wyjściu, to tak:

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

sednic nie robi. jakieś pomysły?

Jak Lambert był na tyle pomocny, by to podkreślić, moje powiedzenie, że sednic nie robi, było niejasne. To, co się dzieje, polega na tym, że program wypisuje stdout(jestem pewien, że nie pisze do stderr), jak zwykle, nawet jeśli jest przesyłany strumieniowo sed.

Problem polega na tym, że polecenie wywołuje drugi program, który następnie wyprowadza na standardowe wyjście. Pierwszy program drukuje kilka wierszy; te mogę edytować. Następnie jest strumień wartości drukowany przez drugi program; tych nie mogę edytować.

Metody Perl i awk również nie działają.

P Jones
źródło
5
Czy stdbuf -o0 command | sed "s/some text/some text bolded/"działa
FloHimself
3
Z „sed robi nic” masz na myśli, że podstawienie nie zostało dokonane lub nie masz żadnych wyników? Poleceniem może być pisanie do stderr zamiast stdout? Jeśli chcesz wyróżnić coś, czego możesz użyćcommand|egrep 'some text|$'
Lambert
Czy polecenie zapisuje na stdout (a nie na stderr)?
Anthon
1
Ponieważ tekst pojawia się więcej niż jeden raz, należy dodać g„globalne” podstawienie, w przeciwnym razie zostanie zastąpione tylko pierwsze wystąpienie w wierszu:sed "s/old/new/g"
ph0t0nix
@ ph0t0nix Nie, to nie jest problem, ale byłby to problem w dalszej kolejności. Dzięki za wskazówkę.
P Jones

Odpowiedzi:

12

Możliwe, że dane wyjściowe polecenia są buforowane. Kiedy polecenie zapisuje na terminalu, bufor jest opróżniany przy każdej nowej linii, więc widać, że pojawia się w oczekiwanym tempie. Kiedy polecenie zapisuje do potoku, bufor jest opróżniany tylko wtedy, gdy osiągnie kilka kilobajtów, więc jest bardzo opóźniony. Tak jest domyślne zachowanie standardowej biblioteki wejścia / wyjścia.

Aby zmusić polecenie, aby nie buforowało wyników, możesz użyć unbuffer(z oczekiwań) lub stdbuf(z GNU coreutils).

unbuffer command | sed …
stdbuf -o0 command | sed …
Gilles „SO- przestań być zły”
źródło
stdbufnie działało (wcześniej wspomniano, BTW), ale działało unbuffer!! Nie masz pojęcia, jak mnie uszczęśliwiłeś.
P Jones
I dziękuję za zwięzłe wyjaśnienie. Bardzo pomocny.
P Jones
sedSam używa takiego bufora, (por ChennyStar post) Tak tu przykłady nie może działać, ponieważ sedjest commanddo unbuffer: cat /etc/passwd | unbuffer sedale sedsama ma -uopcji, więc grepmoże być bardziej Skuteczny w tych przykładach. Wielkie dzięki za informacje w tle! Świetna odpowiedź!
matematyka
4

sed ma na to opcję:

-u, - niebuforowany

Który ładuje minimalne ilości danych z plików wejściowych i częściej opróżnia bufory wyjściowe. Zobacz man sedpo więcej szczegółów.

ChennyStar
źródło
1
sedTylko GNU (nie BSD sed) i uważam, że nadal nie zapobiegałoby to buforowaniu polecenia na początku potoku. Ale dobrze o tym wspomnieć. :)
Wildcard,
stdbuf manpage stwierdza, że ​​„Jeśli COMMAND dostosuje buforowanie swoich standardowych strumieni (na przykład„ tee ”), to zastąpi odpowiednie zmiany przez„ stdbuf ”.” Wygląda na to, że sed może należeć do tej kategorii, ale flaga „-u” sprawi, że będzie przydatny w niebuforowanym łańcuchu rur.
MattSmith
2

Użyłbym awk

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

gdzie

  • /some important stuff/ wybierz ważną linię, jak w sed
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; drukuj na czerwono
    • użyj 32,33 dla zielonego, żółtego ...
    • 1, 2 USD można użyć do wybrania określonego pola
  • inne linie są po prostu drukowane „tak jak są”

kluczową kwestią jest to, że commandpowinny wypłukać linie, ale tak powinno być, jeśli masz dużo wyników.

Archemar
źródło
Uwaga: Pytanie nie mówi, że cała linia powinna być „pogrubiona”, a jedynie „jakiś tekst”. Chociaż możliwe jest zaimplementowanie również tej funkcji w awk(za pomocą sub()lub gsub()), w przypadku tej prymitywnej substytucji sedjest z pewnością odpowiednim narzędziem.
Janis
1

The Perl Way:

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

lub z ciągłą wydajnością:

Skrypt bash dla wyniku cont:

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

Testuj z:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - pogrubienie lub zwiększenie intensywności
  • ${1} - backreferenze
  • \x1b[0m - zresetuj wszystkie atrybuty

Wynik:

Niektóre rzeczy
Niektóre rzeczy
Niektóre rzeczy
Niektóre rzeczy

Więcej kodów ucieczki tutaj .

AB
źródło