Przepuść tylko STDERR przez filtr


82

Czy w bashu jest jakiś sposób na przepuszczenie STDERR przez filtr przed ujednoliceniem go z STDOUT? To znaczy chcę

STDOUT ────────────────┐
                       ├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘

zamiast

STDOUT ────┐
           ├────[ filter ]───> terminal/file/whatever
STDERR ────┘

Odpowiedzi:


64

Oto przykład wzorowany na tym, jak zamienić deskryptory plików w bash . Wynik a.out jest następujący, bez przedrostka „STDXXX:”.

STDERR: stderr output
STDOUT: more regular

./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output

Cytując z powyższego linku:

  1. Najpierw zapisz standardowe wyjście jako & 3 (& 1 jest kopiowane do 3)
  2. Następnie wyślij standardowe wyjście na stderr (& 2 jest kopiowane do 1)
  3. Wyślij stderr do & 3 (stdout) (& 3 jest kopiowane do 2)
  4. zamknij & 3 (& - jest podzielone na 3)

1
Na mnie to nie działa. Wreszcie sprawiam, że to działa 3>&2 2>&1 1>&3-.
aleung

3
Uwaga : zakłada się, że FD 3 nie jest używany i nie cofa zamiany deskryptorów plików 1 i 2, więc nie można przejść dalej do innego polecenia. Zobacz tę odpowiedź, aby uzyskać dalsze szczegóły i obejść. Aby uzyskać dużo czystszą składnię {ba, z} sh, zobacz tę odpowiedź .
Tom Hale

25

TL; DR:

$ cmd 2> >(stderr-filter >&2)

Przykład:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

To zadziała zarówno w bash, jak i zsh. Bash jest obecnie prawie wszechobecny, jednak jeśli naprawdę potrzebujesz (naprawdę okrutnego) rozwiązania dla POSIX sh, zobacz tutaj .


Wyjaśnienie

Zdecydowanie najłatwiejszym sposobem na to jest przekierowanie STDERR przez podstawianie procesów :

Podstawianie procesu umożliwia odniesienie się do wejścia lub wyjścia procesu przy użyciu nazwy pliku. Przybiera formę

>(list)

Lista procesów jest uruchamiana asynchronicznie, a jej wejście lub wyjście pojawia się jako nazwa pliku.

Więc to, co otrzymujesz po podstawieniu procesu, to nazwa pliku.

Tak jak możesz:

$ cmd 2> filename

możesz to zrobić

$ cmd 2> >(filter >&2)

>&2Przekierowania filter„s STDOUT z powrotem do pierwotnego stderr.


1
Jest pewne zastrzeżenie, które wiąże się z tą odpowiedzią: bash nie czeka na zakończenie procesu zastępowania , podczas gdy żonglerka FD w celu zamiany 1 i 2 tak. To może być ważne. Spaliłem się, robiąc to exec 2> >(while .. read .. echo)w skrypcie działającym jako usługa systemd. journald przechwytuje fd2 usługi i wnioskuje poziom dziennika z przedrostka „<N>”: dołączaj <2> przed wierszami wyjściowymi i otrzymujesz poziom ERROR przypisany do rekordów dziennika. Ale czasami systemd szybciej zabijał całą grupę procesów po wyjściu z głównej, zanim zdążył zarejestrować ostatnią, najważniejszą wiadomość!
kkm

Ładne proste rozwiązanie. Uwaga: uważaj, używając tego jako części zadania cron; podstawianie procesów nie jest dostępne w niektórych powłokach używanych przez crona.
Quinn Comendant

24

Wydaje się, że naiwne stosowanie zastępowania procesów umożliwia filtrowanie stderroddzielnie od stdout:

:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err

Zauważ, że stderrpojawia się stderri stdoutwłącza stdout, co możemy zobaczyć, opakowując całość w inną podpowłokę i przekierowując do plików oie

( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e

dlaczego :; na początku? Próbowałem magicznej linii bez i nie wydaje mi się, żeby to miało znaczenie.
Koshmaar,

1
Niektórzy używają $jako wiersza poleceń. Wtedy często piszą powłoki przykłady takich jak: $ cat /var/log/syslog | fgrep .... Jednak tej linii nie można kopiować i wklejać ze względu na rozszerzenie $. :;wygląda jak zachęta, ale w zasadzie jest to powłoka nie działa; dzięki czemu można bezpiecznie zaznaczyć i wkleić całą linię.
solidsnack

23
Ok, ale możesz po prostu pominąć:; a linia byłaby również kopiowalna i wklejana :) Dla mnie:; nie wygląda jak zachęta, nie czyni przykładu jaśniejszym (oddzielając polecenia od wyjścia), ale wprowadza w błąd. Chociaż rozumiem twój punkt widzenia i nie omawiajmy składni / konwencji.
Koshmaar

15

TL; DR: (bash i zsh)

$ cmd 2> >(stderr-filter >&2)

Przykład:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

Wiele odpowiedzi w sieci StackExchange ma postać:

cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'

Ma to wbudowane założenie: ten deskryptor pliku 3 nie jest używany do czegoś innego.

Zamiast tego użyj nazwanego deskryptora pliku i {ba,z}shprzydzieli następny dostępny deskryptor pliku> = 10:

cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'

Zauważ, że nazwane deskryptory plików nie są obsługiwane przez POSIX sh.

Innym problemem związanym z powyższym jest to, że polecenie nie może być przesłane potokiem do dalszych poleceń bez ponownego zamiany STDOUT i STDERR z powrotem na ich oryginalne wartości.

Aby umożliwić shdalsze przesyłanie rur w POSIX (i nadal zakładając, że FD 3 nie jest tego używany), sprawa się komplikuje :

(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1

Tak więc, biorąc pod uwagę założenie i gnarly składnię tego, prawdopodobnie lepiej będzie, jeśli użyjesz prostszej bash/ zshskładni pokazanej w TL; DR powyżej i wyjaśnionej tutaj .


8

Uważam, że podstawienie procesu bash jest łatwiejsze do zapamiętania i używania, ponieważ prawie dosłownie odzwierciedla pierwotny zamiar. Na przykład:

$ cat ./p
echo stdout
echo stderr >&2
$ ./p 2> >(sed -e 's/s/S/') | sed 's/t/T/'
sTdout
STderr

używa pierwszej komendy sed jako filtru tylko na stderr, a drugiej komendy sed do modyfikowania połączonych danych wyjściowych.

Zauważ, że spacja po 2> jest obowiązkowa, aby polecenie zostało poprawnie przeanalizowane.


5

Ostatnia część tej strony podręcznika Advanced Bash Scripting Guide to „przekierowywanie tylko stderr do potoku”.

# Przekierowywanie tylko stderr do potoku.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Dzięki, SC

To może być to, czego chcesz. Jeśli nie, jakaś inna część ABSG powinna ci pomóc, to jest doskonałe.


Wahamy się, czy polecić ABSG jako punkt odniesienia, ponieważ łączy on dokumentację, receptę i opinię bez wyraźnego zaznaczania różnic. Niektóre sekcje mają również wątpliwą zawartość, chociaż ta, do której prowadzą linki, wydaje się w porządku.
tripleee

3

Spójrz na nazwane potoki:

$ mkfifo err
$ cmd1 2>err |cat - err |cmd2

nie cat - errprzerwie przeplatania się stdout i stderr?
Martin DeMello

@Martin - to zależy. Jeśli cmd1, cat lub cmd2 buforują dane wyjściowe, możesz zobaczyć wynik poza kolejnością.
mob
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.