Czy to literówka w sekcji przekierowań instrukcji Bash?


13
Note that the order of redirections is significant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error to the file dirlist, 
   while the command

          ls 2>&1 > dirlist

   directs  only  the  standard  output  to  file  dirlist,  because the 
   standard error was duplicated from the standard output before the standard
   output was redirected to dirlist.

Ta ostatnia część jest dla mnie myląca. W takim przypadku każdy błąd standardowy zostanie wydrukowany na terminalu, a każdy STDOUT przejdzie do pliku dirlist. Tak by się stało, ale nie tak rozumiem instrukcję.

Wygląda na to, że powinien powiedzieć „ponieważ błąd standardowy został zduplikowany ze standardowego wyjścia PO, gdy standardowe wyjście zostało przekierowane do dirlist”. Jeśli STDERR został wysłany do STDOUT zanim STDOUT został skierowany do pliku, to czy plik nie zawiera STDOUT I STDERR?

Czy ktoś może mi to wyjaśnić? Czy to z mojej strony słabe rozumienie czytania? W tym kontekście użycie duplikacji słów wydaje mi się trochę dziwne. Być może to mnie rzuca.



1
Klasyczny przypadek mieszania operacji, które są „według wartości” vs „przez odniesienie”. Po skopiowaniu deskryptora pliku jest to operacja według wartości . W programowaniu, po tym, jak a = 1; b = a; a = 2spodziewasz a == 2 && b == 1się prawdy. Przekierowanie 2>&1jest podobne do b = aprzypisania - odbywa się według wartości, a nie przez odniesienie. 2>&1nie łączy deskryptora pliku 2 z deskryptorem pliku 1 na całą wieczność - wciąż są 2 odrębnymi deskryptorami plików, które przypadkiem wskazują ten sam plik.
jw013

Odpowiedzi:


23

Powielanie jest tutaj naprawdę ważną częścią.

Zobaczmy, dokąd zmierzają deskryptory plików przed przekierowaniem. Zwykle jest to aktualny terminal, np .:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

Teraz, jeśli dzwonimy ls -lbez przekierowania, komunikaty wyjściowe i komunikaty o błędach trafiają do mojego terminala pod /dev/pts/1.

Jeśli najpierw przekierujemy STDOUTdo pliku ( ls -l > dirlist), wygląda to tak:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Kiedy następnie przekierować STDERRdo dwóch egzemplarzach z STDOUT„s deskryptorze pliku ( ls -l > dirlist 2>&1), STDERRprzechodzi do duplikat /home/bon/dirlist:

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

Jeśli najpierw przekierowalibyśmy STDERRdo duplikatu STDOUTdeskryptora pliku ( ls -l 2>&1):

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

a następnie STDOUT do file ( ls -l 2>&1 > dirlist), otrzymalibyśmy to:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Tutaj STDERRnadal jedzie do terminala.

Widzisz, kolejność na stronie podręcznika jest poprawna.


Testowanie przekierowania

Teraz możesz to sprawdzić samodzielnie. Używając ls -l /proc/$$/fd/, zobaczysz, gdzie STDOUT(z fd 1) i STDERR(z fd 2) idą do bieżącego procesu:

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

Utwórzmy mały skrypt powłoki, który pokazuje, gdzie są skierowane deskryptory plików. W ten sposób zawsze otrzymujemy stan podczas połączenia ls, w tym wszelkie przekierowania z powłoki wywołującej.

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(Za pomocą CtrlDwysyłasz plik na końcu pliku, a więc przerywasz catczytanie komendy od STDIN.)

Teraz wywołaj ten skrypt z różnymi kombinacjami przekierowań:

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

Widać, że deskryptory plików 1 (dla STDOUT) i 2 (dla STDERR) różnią się. Dla zabawy możesz również przekierować STDINi zobaczyć wynik:

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(Pytanie do czytelnika: Gdzie wskazuje deskryptor pliku 255? ;-))


+1 - doskonała odpowiedź. Niezwykle dobrze napisane i wspaniałe przykłady. Dziękuję Ci!!!
slm

Rozumiem, myślę, że moje nieporozumienie polegało na tym, że przekierowanie będzie trwałe dla wszystkich następnych poleceń, tak że dowolny STDERR dla reszty wiersza przejdzie do STDOUT.
Gregg Leventhal

2

Nie, instrukcja ma rację.

Jeśli na początku 1 wskazuje na terminal, a 2 także na terminal, to:

command  2>&1   1>somewhere

ocena przekierowania nastąpi od lewej do prawej.

Więc PIERWSZA oceni 2>&1, a zatem PIERWSZA skopiuje to, na co 1wskazywał fd (tj. Deskryptor pliku the terminal, zwykle / dev / tty) do fd 2.

W tym momencie fd 2wskazuje teraz, gdzie fd 1wskazywał na ( the terminal)

A następnie ocenia 1>somewhereczęść, a zatem skopiuje deskryptor pliku z somewherefd 1(więc w tym momencie fd 1wskazuje teraz somewhere, a fd 2nadal wskazuje the terminal)

Tak więc rzeczywiście wypisuje 1 do „gdzieś” i 2 do terminala, ponieważ 2 zostało zduplikowane z 1 PRZED 1 zmieniono.

Drugie zamówienie:

command  1>somewhere 2>&1

najpierw przekieruje fd 1do somewhere, a następnie skopiuje to samo odwołanie do fd 2, więc na końcu 2 również wskazuje somewhere. Ale odtąd nie są one „powiązane”. Każdą z nich nadal można przekierować osobno.

dawny:

command  1>somewhere 2>&1
exec 2>/dev/null

Na końcu tego, fd 1wskazuje na somewhere, a fd 2jest skierowane na/dev/null

Zwykłe nazwy dla fd 1to STDOUT (standardowe wyjście), a zwykła nazwa dla fd 2to STDERR (błąd standardowy, ponieważ jest powszechnie używany do wyświetlania błędów bez zakłócania STDOUT)


@ Michael-mrozek: dziękuję za edycję, ale nalegam na powiedzenie „kopiuj” zamiast „duplikuj”, ponieważ „duplikat” może doprowadzić do przekonania, że ​​odtąd oba są „tym samym”, co nie jest prawdą. np . cmd 1>somewhere 2>&1 ; exec 2>/dev/null: po exec, tylko 2 zostało przekierowane do / dev / null (1 nadal będzie „gdzieś”). Potrzebuję pomocy, aby wymyślić sposób, aby powiedzieć „co wskazuje 1” zamiast „fd 1”, jednak… ponieważ to też jest mylące…
Olivier Dulac

1
Nie jestem pewny co masz na myśli; to ty zmieniłeś go z „kopiuj” na „duplikuj”. Wszystko, co zrobiłem, to wielkie litery i formatowanie, nie zmieniłem ani słowa
Michael Mrozek

doh ... ^^ przepraszam. I ponownie zredagowałem, aby przeformułować, aby sprecyzować, co jest kopiowane do czego ^^
Olivier Dulac

1

Myślę, że mylącą częścią tutaj jest nieporozumienie, że przekierowanie stderr na stdout faktycznie łączy oba strumienie.

Zupełnie rozsądnym pomysłem, ale to, co się dzieje, gdy piszesz, 2>&1to stderr bierze peekaboo na to, co stdout pisze i pisze w tym samym miejscu. Dlatego jeśli później powiesz stdout, aby poszedł napisać gdzie indziej, nie będzie to miało wpływu na miejsce docelowe stderr, które zostało już przeniesione.

Myślę, że sam jest to trochę sprzeczne z intuicją, ale tak to działa. Skonfiguruj najpierw, gdzie chcesz pisać, a następnie powiedz wszystkim „skopiuj mnie”. Nadzieja, która wyjaśnia ...


0

POWIELANIE...

jest ważne, ale raczej w tym sensie, że jest źródłem wielu nieporozumień . To jest naprawdę bardzo proste. Ta odpowiedź jest tylko „radykalną” ilustracją.

Przyjęta odpowiedź jest dobra, ale zbyt długa i podkreśla „powielanie”.

Q mądrze kończy się na:

W tym kontekście użycie duplikacji słów wydaje mi się trochę dziwne. Być może to mnie rzuca.

Używam notacji bash i definiuję zmienne „one” i „two” jako uchwyty plików „1” i „2”. Operator przekierowania (wyjściowy) >jest przypisaniem =. &i $oznacza „wartość” z.

Przykłady man bash (z dodaną domyślną „1”)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

stają się:

one=dirlist  two=$one

i

two=$one   one=dirlist

I nawet to nie jest dla mnie automatyczne i, jak sądzę, niektóre inne. Pierwszy wiersz zostawia cię $onei $twooba zawierają „dirlist”. Oczywiście.

Druga linia zaczyna się od niepotrzebnego przypisania. Oba zaczynają się od definicji z „TTY” (nieco symbolicznym) jako ich kierunkiem ; to przypisanie nie zmienia żadnej wartości, a przy zmiennych jak w uchwytach plików nic nie jest magicznie powiązane. Na zmienne twonie mają wpływu następujące elementy one=dirlist. Oczywiście nie.

Sombody tutaj (6 lat temu) zasugerował „wskaż” zamiast „kopiuj” lub „duplikuj”, a potem zdał sobie sprawę: to też byłoby mylące.

To semantyczne powielanie lub wskaźnik nie jest nawet potrzebne. Może to ampersand wymaga więcej uwagi. „Wartość” operatora / tokena / cokolwiek.

Jeśli - i tylko jeśli - szukasz sposobu na uzyskanie zaskakującego numeru zadania na konsoli , wtedy komunikat „gotowe” plus jako bonus plik o nazwie „2”, to:

ls 1>2& 2>/dev/null

Odczytuje naturalnie jako „ kopiuj” / „duplikuj” 1 do 2, a następnie oba razem do wartości null . Ale pomysł jest zły, a także składnia. (ale nie ma błędu składniowego, jest poprawny)

Właściwy sposób na jego zaplanowanie to przekierowanie dowolnego z dwóch na zero, a następnie przekierowanie INNEGO do SAMEGO miejsca:

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(początkowe „1” można pominąć)

(OK, acc. A nie jest zbyt długi, ale to zbyt duża lista - lub: bardzo dobra wizualizacja, niezbyt dobre wyjaśnienie)

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.