Starałem się przekierować zarówno stdout
i stderr
do pliku dzisiaj, i natknąłem się na to:
<command> > file.txt 2>&1
To najwyraźniej przekierowuje stderr
do stdout
pierwszego, a następnie wynikowy stdout
przekierowuje do file.txt
.
Dlaczego jednak zamówienie nie jest <command> 2>&1 > file.txt
? Można by to oczywiście odczytać jako (zakładając wykonanie od lewej do prawej) najpierw wykonywane polecenie, stderr
przekierowanie do, stdout
a następnie stdout
zapisanie do wynikowego file.txt
. Ale powyższe tylko przekierowuje stderr
do ekranu.
Jak powłoka interpretuje oba polecenia?
command-line
bash
redirect
Pociąg Heartnet
źródło
źródło
bash
instrukcji . Nawiasem mówiąc, przekierowania nie są poleceniami.execv
wywoływana jest funkcja -family syscall w celu przekazania podprocesu uruchamianej komendzie, powłoka jest poza pętlą (kod nie wykonuje już kodu w tym procesie ) i nie ma możliwości kontrolowania tego, co dzieje się od tego momentu; dlatego wszystkie przekierowania muszą zostać wykonane przed rozpoczęciem wykonywania, podczas gdy powłoka ma kopię samego siebie uruchomioną w procesie, wfork()
którym uruchomiono polecenie.Odpowiedzi:
Kiedy uruchomisz
<command> 2>&1 > file.txt
stderr, zostaniesz przekierowany2>&1
do tego, dokąd aktualnie idzie stdout, twój terminal. Następnie stdout jest przekierowywany do pliku przez>
, ale stderr nie jest z nim przekierowywany, więc pozostaje jako wyjście terminala.Z
<command> > file.txt 2>&1
stdout jest najpierw przekierowywany do pliku przez>
, a następnie2>&1
przekierowuje stderr do dokąd zmierza stdout, czyli do pliku.Na początku może wydawać się to sprzeczne z intuicją, ale kiedy pomyślisz o przekierowaniach w ten sposób i pamiętasz, że są one przetwarzane od lewej do prawej, ma to o wiele większy sens.
źródło
Może to mieć sens, jeśli go wyśledzisz.
Na początku stderr i stdout idą do tego samego (zazwyczaj terminalu, który tu nazywam
pts
):Mam tu na myśli stdin, stdout i stderr po ich numerach deskryptorów plików : są to odpowiednio deskryptory plików 0, 1 i 2.
Teraz w pierwszym zestawie przekierowań mamy
> file.txt
i2>&1
.Więc:
> file.txt
:fd/1
teraz idzie dofile.txt
. Z>
,1
to domyślny deskryptor pliku, gdy nic nie jest określone, więc jest to1>file.txt
:2>&1
:fd/2
Teraz idzie tam, gdziefd/1
obecnie idzie:Z drugiej strony, przy
2>&1 > file.txt
odwróceniu kolejności:2>&1
:fd/2
teraz trafia do dowolnego miejscafd/1
, co oznacza, że nic się nie zmienia:> file.txt
:fd/1
teraz idzie dofile.txt
:Ważne jest to, że przekierowanie nie oznacza, że przekierowany deskryptor pliku będzie śledził wszystkie przyszłe zmiany deskryptora pliku docelowego; przybierze tylko aktualny stan.
źródło
2.
drugiej części;fd/1 -> file.txt
i niefd/2 -> file.txt
.Myślę, że to pomaga myśleć, że powłoka najpierw skonfiguruje przekierowanie po lewej stronie i ukończy go przed skonfigurowaniem następnego przekierowania.
Wiersz poleceń Linuksa autorstwa Williama Shottsa mówi
to ma sens, ale wtedy
ale w rzeczywistości możemy przekierować stdout na stderr po przekierowaniu stderr do pliku z tym samym efektem
Tak więc w
command > file 2>&1
powłoka wysyła stdout do pliku, a następnie wysyła stderr do stdout (który jest wysyłany do pliku). Podczas gdy wcommand 2>&1 > file
powłoce najpierw przekierowuje stderr na stdout (tzn. Wyświetla go w terminalu, gdzie normalnie idzie stdout), a następnie przekierowuje stdout do pliku. TLCL wprowadza w błąd mówiąc, że najpierw musimy przekierować stdout: ponieważ możemy najpierw przekierować stderr do pliku, a następnie wysłać do niego stdout. To, czego nie możemy zrobić, to przekierowanie stdout na stderr lub odwrotnie przed przekierowaniem do pliku. Inny przykładMożna by pomyśleć, że to usunie stdout w to samo miejsce co stderr, ale tak nie jest, najpierw przekierowuje stdout do stderr (ekranu), a następnie tylko przekierowuje stderr, tak jak wtedy, gdy próbowaliśmy go odwrotnie ...
Mam nadzieję, że przyniesie to trochę światła ...
źródło
Masz już kilka bardzo dobrych odpowiedzi. Chciałbym jednak podkreślić, że w grę wchodzą dwie różne koncepcje, których zrozumienie ogromnie pomaga:
Tło: deskryptor pliku a tabela plików
Twój deskryptor pliku to tylko liczba 0 ... n, która jest indeksem w tabeli deskryptorów plików w twoim procesie. Zgodnie z konwencją STDIN = 0, STDOUT = 1, STDERR = 2 (zwróć uwagę, że
STDIN
tutaj terminy itp. Są tylko symbolami / makrami używanymi w konwencji w niektórych językach programowania i stronach podręcznika , nie ma rzeczywistego „obiektu” o nazwie STDIN; dla celem tej dyskusji, STDIN jest 0 itd.).Ta tabela deskryptorów plików sama w sobie nie zawiera żadnych informacji na temat rzeczywistego pliku. Zamiast tego zawiera wskaźnik do innej tabeli plików; ten ostatni zawiera informacje o rzeczywistym pliku fizycznym (lub urządzeniu blokowym, potoku lub czymkolwiek innym, co Linux może rozwiązać za pomocą mechanizmu plików) i więcej informacji (tj. czy to do odczytu, czy zapisu).
Więc kiedy używasz
>
lub<
w swojej powłoce, po prostu zastępujesz wskaźnik odpowiedniego deskryptora pliku, aby wskazać coś innego. Składnia2>&1
po prostu wskazuje deskryptor 2 na dowolne 1 punkty.> file.txt
po prostu otwiera sięfile.txt
do pisania i pozwala STDOUT (plik decsriptor 1) wskazywać na to.Istnieją inne zalety, np.
2>(xxx)
( Np . : stwórz nowy procesxxx
, stwórz potok, podłącz deskryptor pliku 0 nowego procesu do końca odczytu potoku i podłącz deskryptor pliku 2 oryginalnego procesu do końca zapisu rura).Jest to również podstawa dla „magii obsługi plików” w innym oprogramowaniu niż twoja powłoka. Na przykład możesz w swoim skrypcie Perl
dup
powiązać deskryptor pliku STDOUT z innym (tymczasowym), a następnie ponownie otworzyć STDOUT dla nowo utworzonego pliku tymczasowego. Od tego momentu wszystkie dane wyjściowe STDOUT z własnego skryptu Perla i wszystkiesystem()
wywołania tego skryptu znajdą się w tym pliku tymczasowym. Podup
zakończeniu możesz przywrócić STDOUT do tymczasowego deskryptora, w którym go zapisałeś, i presto, wszystko jest jak poprzednio. W międzyczasie możesz nawet pisać do tego tymczasowego deskryptora, więc podczas gdy twoje rzeczywiste wyjście STDOUT trafia do pliku tymczasowego, nadal możesz faktycznie wypisywać rzeczy do prawdziwego STDOUT (zwykle użytkownika).Odpowiedź
Aby zastosować podane powyżej informacje podstawowe do pytania:
Z lewej na prawą.
fork
od nowego procesu.file.txt
i zapisz jego wskaźnik w deskryptorze pliku 1 (STDOUT).file.txt
oczywiście otwarte ).exec
<command>
Miałoby to sens, gdyby istniał tylko jeden stół, ale jak wyjaśniono powyżej, są dwa. Deskryptory plików nie wskazują na siebie rekurencyjnie, nie ma sensu myśleć „przekieruj STDERR do STDOUT”. Prawidłowa myśl to „wskaż STDERR tam, gdzie wskazuje STDOUT”. Jeśli zmienisz STDOUT później, STDERR pozostanie tam, gdzie jest, nie magicznie idzie w parze z kolejnymi zmianami w STDOUT.
źródło
Kolejność jest od lewej do prawej. Podręcznik Bash zawiera już to, o co prosisz. Cytat z
REDIRECTION
części instrukcji:i kilka linii później:
Ważne jest, aby pamiętać, że przekierowanie jest rozwiązywane najpierw przed uruchomieniem jakichkolwiek poleceń! Zobacz https://askubuntu.com/a/728386/295286
źródło
Zawsze jest od lewej do prawej ... z wyjątkiem kiedy
Podobnie jak w matematyce wykonujemy od lewej do prawej, z tym wyjątkiem, że mnożenie i dzielenie odbywa się przed dodawaniem i odejmowaniem, z wyjątkiem operacji wewnątrz nawiasów (+ -), które byłyby wykonywane przed mnożeniem i dzieleniem.
Zgodnie z przewodnikiem Bash dla początkujących tutaj ( Bash Beginners Guide ) istnieje 8 rzędów hierarchii tego, co jest pierwsze (przed od lewej do prawej):
Więc zawsze jest od lewej do prawej ... z wyjątkiem sytuacji, gdy ...
źródło