Rurociągi STDERR vs. STDOUT

24

Zgodnie z „ Linux: The Complete Reference 6th Edition ” (str. 44), możesz przesyłać tylko STDERR za pomocą |&symboli przekierowania.

Napisałem dość prosty skrypt, aby to przetestować:

#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2

Uruchamiam ten skrypt w następujący sposób:

./script.sh |& sed 's:^:\t:'

Przypuszczalnie tylko wcięte zostaną linie wydrukowane do STDERR. Jednak tak naprawdę nie działa tak, jak widzę:

    Normal Text.
    Error Text. 

Co robię tutaj źle?

Naftuli Kay
źródło

Odpowiedzi:

26

Nie wiem, z jakiego tekstu korzysta Twoja książka, ale instrukcja bash jest jasna (jeśli znasz już przekierowania):

Jeśli |&jest używany, błąd standardowy polecenia 1, oprócz standardowego wyjścia, jest podłączony do standardowego wejścia polecenia 2 przez potok; jest skrótem od 2>&1 |. To niejawne przekierowanie standardowego błędu na standardowe wyjście jest wykonywane po wszelkich przekierowaniach określonych przez polecenie.

Więc jeśli nie chcesz mieszać standardowego wyjścia ze standardowym błędem, będziesz musiał przekierować standardowe wyjście gdzie indziej. Zobacz Jak grep standardowy strumień błędów (stderr)?

{ ./script.sh 2>&1 >&3 | sed 's:^:\t:'; } 3>&1

Zarówno fd 1 i 3 script.shi sedskieruje do pierwotnego przeznaczenia stdout jednak. Jeśli chcesz być dobrym obywatelem, możesz zamknąć te fd 3, których te polecenia nie potrzebują:

{ ./script.sh 2>&1 >&3 3>&- | sed 's:^:\t:' 3>&-; } 3>&1

bashi ksh93może skondensować >&3 3>&-to >&3-(ruch fd).

Gilles „SO- przestań być zły”
źródło
Podręcznik bash nie jest dla mnie całkowicie jasny. Nie bardzo rozumiem, dlaczego smth jak ./script.sh > /tmp/stdout_goes_here |& grep 'grepping_script_stderr'nie działa zgodnie z przeznaczeniem, czyli: przekierowanie script.sh„s stdout(który, zgodnie z instrukcją fragmencie powinno nastąpić wcześniej), następnie pozostawić grepdo przetwarzania skryptu stderr. Zamiast tego stderroba tdout` kończą się wstdout_goes_here
sxc731
1
@ sxc731 |&jest skrótem dla 2>&1 |. >/tmp/stdout_goes_here |&Przekierowuje więc stdout na /tmp/stdout_goes_here, a następnie 2>&1przekierowuje stderr na dokądkolwiek zmierza stdout, czyli w /tmp/stdout_goes_herekońcu |nie otrzymuje żadnych danych wejściowych, ponieważ wyjście polecenia zostało przekierowane. Należy pamiętać, że >&1przekierowuje do dowolnego miejsca, w którym deskryptor pliku 1 aktualnie się znajduje , a nie do dowolnego miejsca, w którym skończy się deskryptor pliku 1 . Aby przesłać tylko stderr i przekierować standardowe wyjście do pliku, jednym ze sposobów jest 2>&1 >/tmp/stdout_goes_here |.
Gilles „SO- przestań być zły”
6

|&potoki stderr do stdin, tak 2>&1 |, więc następny program dostanie oba na stdin.

$cat test.sh
#!/bin/bash
echo "Normal Text."
echo "Error Text." >&2
$./test.sh | sed 's:^:\t:'
Error Text.
        Normal Text.
$ ./test.sh |& sed 's:^:\t:'
        Normal Text.
        Error Text.
Kevin
źródło
Och, więc jest to w zasadzie odpowiednik dłuższego wyrażenia runcommand 2>&1 | tee? tj. runcommand |& tee?
Naftuli Kay 10.11.11
tak, są takie same.
Kevin,
1

|&w bash jest po prostu (niezbyt przenośnym) skrótem 2>&1 |, więc powinieneś zobaczyć wcięcie każdej linii.

jw013
źródło