2012-07-19 12 views
10

Podczas łączenia stderr ze stdout, dlaczego musi przyjść przed | (rura), ale po > myfile (przekierowanie do pliku)?Dlaczego 2> i 1 muszą nadejść przed | (pipe), ale po "> myfile" (przekierowanie do pliku)?

Aby przekierować stderr do stdout do pliku wyjściowego:

echo > myfile 2>&1 

Aby przekierować stderr do stdout dla rury:

echo 2>&1 | less 



Moim założeniem było, że mogę po prostu zrobić:

echo | less 2>&1 

i to woul działa, ale tak nie jest. Dlaczego nie?

Odpowiedz

16

rurociąg jest | -delimited lista poleceń. Wszelkie podane przekierowania mają zastosowanie do komend składowych (prostych lub złożonych), ale nie do potoku jako całości. Każdy łańcuch łańcuchów powoduje jedno polecenie standardowe do wejścia standardowego następnego przez niejawne zastosowanie przekierowania do każdej podpowłoki przed oszacowaniem jakichkolwiek przekierowań związanych z poleceniem.

cmd 2>&1 | less 

pierwsze standardowe wyjście pierwszego podpowłoce jest przekierowany do rury, z której less czytającego. Następnie przekierowanie 2>&1 zostanie zastosowane do pierwszego polecenia. Przekierowanie stderr na stdout działa, ponieważ stdout już wskazuje na potok. To jest przekierowanie do less. Less's stdout i stderr prawdopodobnie zaczęły wskazywać na terminal, więc w tym przypadku nie ma to żadnego wpływu.

Jeśli chcesz przekierowanie zastosowanie do całego rurociągu, do wielu poleceń grupowych jako część rurociągu lub rurociągów gniazdo, a następnie użyć grupy poleceń (lub innego związek polecenie):

{ { cmd1 >&3; cmd2; } 2>&1 | cmd3; } 3>&2 

Może być typowym przykładem. Końcowy wynik to: stderr cmd1 i cmd2 ->cmd3; cmd2 's stdout ->cmd3; i cmd1 i cmd3 's stdout -> terminal.

Jeśli używasz bash specyficzne |& rury, robi się obcy, ponieważ każdy z stdout rurociągu przekierowuje nadal występuje pierwszy, ale przekierowanie stderr faktycznie przychodzi ostatni. Na przykład:

f() { echo out; echo err >&2; }; f >/dev/null |& cat 

Teraz, wbrew treści, wszystkie dane wyjściowe są ukryte. Pierwszy numer z f trafia do potoku, następny stdout z f jest przekierowywany na /dev/null, a na końcu stderr jest przekierowywany do stdout (/dev/null).

Polecam nigdy nie używać |& w Bash - służy tutaj do demonstracji.

+3

+1 dobrze wyjaśnione – jordanm

+2

+1. Dodam tylko, że rura jest separatorem poleceń, podobnie jak średnik. –

+1

+1 Wow, doskonała odpowiedź @ormaaj! Dokładnie tego, czego szukałem - dziękuję! –

6

Aby dodać odpowiedź ormaaj za:

Powodem trzeba określić operatorów przekierowania w odpowiedniej kolejności jest to, że są one obliczane od lewej do prawej. Rozważmy następujące listy poleceń:

# print "hello" on stdout and "world" on stderr 
{ echo hello; echo world >&2; } 

# Redirect stdout to the file "out" 
# Then redirect stderr to the file "err" 
{ echo hello; echo world >&2; } > out 2> err 

# Redirect stdout to the file "out" 
# Then redirect stderr to the (already redirected) stdout 
# Result: all output is stored in "out" 
{ echo hello; echo world >&2; } > out 2>&1 

# Redirect stderr to the current stdout 
# Then redirect stdout to the file "out" 
# Result: "world" is displayed, and "hello" is stored in "out" 
{ echo hello; echo world >&2; } 2>&1 > out 
2

Moja odpowiedź brzmi przez zrozumienie deskryptorów plików. Każdy proces ma kilka deskryptorów plików: wpisy do plików, które są otwarte. Domyślnie numer 0 jest dla stdin, numer 1 jest dla standardowego wyjścia, a numer 2 jest dla stderr.

Domyślnie przekierowania i/lub < łączą się z najbardziej uzasadnionymi deskryptorami plików, grubymi i standardowymi. Jeśli przekierujesz standardowe wyjście do pliku (jak w przypadku foo > bar), podczas uruchamiania procesu 'foo', otwierany jest plik 'bar' do zapisu i podpięty na deskryptor pliku o numerze 1. Jeśli chcesz mieć tylko stderr w 'barze', użyjesz foo 2> bar, który otwiera pasek plików i przechwytuje go na stderr.

Teraz usługa readresatora "2> & 1". Zwykle czytałem to jako "deskryptor pliku 2 do tego samego, co deskryptor pliku 1. Podczas odczytywania wiersza poleceń od lewej do prawej, możesz wykonać następną: foo 1>bar 2>&1 1>/dev/tty. Tym samym deskryptor pliku 1 jest ustawiony na plik "pasek", deskryptor pliku 2 jest ustawiony na 1 (stąd "pasek"), a następnie deskryptor pliku 1 jest ustawiony na/dev/tty. Runnning foo wysyła swoje wyjście do/dev/tty i wysyła stderr do pliku 'bar'.

Teraz pojawia się potok: nie zmienia to deskryptorów plików, jednak połączy je pomiędzy procesami: stdout lewego procesu ot stdin następnego. Stderr zostaje przekazany dalej. Dlatego jeśli chcesz, aby potok działał tylko na stderr, użyjesz foo 2| bar, który połączy stderr z foo z stdin z bar. (Nie jestem pewien, co się dzieje z stdout foo).

z powyższym, w przypadku korzystania foo 2>&1 | bar, ponieważ stderr od foo jest ponownie kierowany do stdout foo, zarówno stdout i stderr z foo przybyć u stdin z bar.

Powiązane problemy