Mylisz się co do stdin, stdout i stderr?

230

Jestem raczej mylony z celem tych trzech plików. Jeśli dobrze rozumiem, stdinto plik, w którym program zapisuje swoje żądania uruchomienia zadania w tym procesie, stdoutto plik, w którym jądro zapisuje dane wyjściowe, a proces żądający dostępu do informacji z niego stderrjest plikiem w których wpisane są wszystkie wyjątki. Po otwarciu tych plików, aby sprawdzić, czy rzeczywiście się zdarzają, nic nie sugeruje!

Chciałbym wiedzieć, jaki jest dokładnie cel tych plików, absolutnie głupia odpowiedź z bardzo małym żargonem technologicznym!

Shouvik
źródło
36
Uwaga: To pytanie było możliwe do zaakceptowania w 2010 r., Ale w dzisiejszych czasach zostanie bardzo szybko ocenione.
byxor
3
@Brandon Czy możesz podać powód? Myślę, że byłoby to przydatne dla twojego komentarza.
Niezależny
3
@ byxor, żeby być uczciwym, zapytam: czy post był z prośbą o pomoc w debugowaniu kodu? wydaje się, że Shouvik zadał pytanie dotyczące celu stdin, stdout i stderr. post op wydaje się być z ciekawości, nie? (Właściwie sam się o tym dowiaduję. dzięki, SO, że nie
usunąłem
2
@ user123456 masz rację. Uczyłem się, jak być programistą, a wtedy S / O było świetnym miejscem do nauki programowania. Początkowo chcieliśmy, aby była to serwis typu wiki dla wszystkich pytań dotyczących nauk komputerowych. #juniorDevForLife
Shouvik
3
@Shouvik dziękuje za ten kawałek historii. Uczę się też, jak zostać programistą (właśnie zostałem przyjęty do fajnego obozu w SF). Nadal jestem dość nowy w S / O i wciąż nie jestem pewien, co mogę, a czego nie mogę opublikować. Uważam, że moderacja tutaj może być dość surowa. Podoba mi się ten hash. #juniorDevForLife. Chciałbym cię skomentować zamiast komentować tutaj, ponieważ nic to nie dodaje do dyskusji, ale nie wierzę, że S / O ma system pm. Miłego dnia.
sansae,

Odpowiedzi:

251

Standardowe dane wejściowe - jest to uchwyt pliku odczytywany przez proces w celu uzyskania od ciebie informacji.

Standardowe wyjście - twój proces zapisuje normalne informacje do tego uchwytu pliku.

Błąd standardowy - proces zapisuje informacje o błędzie do tego uchwytu pliku.

To jest tak głupie, jak tylko mogę :-)

Oczywiście dzieje się tak głównie na podstawie konwencji. Nic nie stoi na przeszkodzie, aby zapisać informacje o błędzie na standardowym wyjściu, jeśli chcesz. Możesz nawet całkowicie zamknąć trzy uchwyty plików i otworzyć własne pliki dla operacji we / wy.

Kiedy proces się rozpocznie, powinien już mieć otwarte uchwyty i może po prostu czytać i / lub pisać do nich.

Domyślnie są one prawdopodobnie podłączone do twojego urządzenia końcowego (np. /dev/tty), Ale powłoki pozwolą ci skonfigurować połączenia między tymi uchwytami a określonymi plikami i / lub urządzeniami (lub nawet potokami do innych procesów) przed rozpoczęciem procesu (niektóre z możliwe manipulacje są dość sprytne).

Przykładem jest:

my_prog <inputfile 2>errorfile | grep XYZ

które będą:

  • stworzyć proces dla my_prog.
  • otwórz inputfilejako standardowe wejście (uchwyt pliku 0).
  • otwórz errorfilejako standardowy błąd (uchwyt pliku 2).
  • utwórz kolejny proces dla grep.
  • dołącz standardowe wyjście my_progdo standardowego wejścia grep.

Re twój komentarz:

Kiedy otwieram te pliki w folderze / dev, dlaczego nigdy nie widzę danych wyjściowych uruchomionego procesu?

To dlatego, że nie są normalnymi plikami. Podczas gdy UNIX prezentuje gdzieś wszystko jako plik w systemie plików, nie robi to tak na najniższych poziomach. Większość plików w /devhierarchii to urządzenia znakowe lub blokowe, w rzeczywistości sterownik urządzenia. Nie mają rozmiaru, ale mają główny i mniejszy numer urządzenia.

Po ich otwarciu następuje połączenie ze sterownikiem urządzenia, a nie z plikiem fizycznym, a sterownik urządzenia jest wystarczająco inteligentny, aby wiedzieć, że osobne procesy powinny być obsługiwane osobno.

To samo dotyczy systemu /procplików Linux . To nie są prawdziwe pliki, tylko ściśle kontrolowane bramy do informacji jądra.

paxdiablo
źródło
1
To za twoją odpowiedź. Chociaż rozumiem cel plików na podstawie tego, co opisujesz, chciałbym przenieść poziom bardziej. kiedy otwieram te pliki w folderze / dev, dlaczego nigdy nie widzę danych wyjściowych uruchomionego procesu. Powiedzmy, że wykonuję top na terminalu, czy nie powinien on okresowo wypisywać swoich wyników do pliku standardowego, dlatego kiedy jest aktualizowany, powinienem być w stanie zobaczyć wystąpienie wydruku wydrukowanego na tym pliku. Ale tak nie jest ... Więc te pliki nie są takie same (te w katalogu / dev).
Shouvik,
7
Ponieważ nie są to technicznie pliki. Są to węzły urządzeń, wskazujące konkretne urządzenie do zapisu. UNIX może przedstawiać ci wszystko jako abstrakcję pliku, ale to nie czyni tego na najgłębszych poziomach.
paxdiablo
1
Skorzystaj z możliwości przekierowania powłoki. xyz >xyz.outzapisze twoje standardowe wyjście w fizycznym pliku, który może być odczytany przez inne procesy. xyz | grep somethingpołączy xyzstdout ze grepstdin bardziej bezpośrednio. Jeśli chcesz mieć nieograniczony dostęp do procesu, którego nie kontrolujesz w ten sposób, musisz zajrzeć do czegoś takiego /proclub napisać kod, aby przefiltrować dane wyjściowe, w jakiś sposób podpinając się do jądra. Mogą istnieć inne rozwiązania, ale wszystkie są prawdopodobnie tak samo niebezpieczne :-)
paxdiablo
20
@Shouvik, zwróć uwagę, że /dev/stdinjest to dowiązanie symboliczne /proc/self/fd/0- pierwszy deskryptor pliku, który ma aktualnie uruchomiony program. To, na co wskazuje /dev/stdin, zmieni się z programu na program, ponieważ /proc/self/zawsze wskazuje na „aktualnie uruchomiony program”. (Niezależnie od tego, który program wykonuje openwywołanie). /dev/stdinI przyjaciele zostali tam umieszczeni, aby uczynić skrypty powłoki setuid bezpieczniejszymi, i pozwolić przekazać nazwę pliku /dev/stdindo programów, które działają tylko z plikami, ale chcesz kontrolować bardziej interaktywnie. (Pewnego dnia będzie to przydatna sztuczka, którą warto poznać.)
sarnold
1
@ CarlosW.Mercado, plik jest fizyczną manifestacją danych. Na przykład bity przechowywane na dysku twardym. Uchwyt pliku to (zwykle) mały token używany do odwoływania się do tego pliku po jego otwarciu.
paxdiablo
62

Bardziej poprawne byłoby powiedzenie tego stdin, stdouti stderrsą to „strumienie we / wy”, a nie pliki. Jak zauważyłeś, te podmioty nie istnieją w systemie plików. Ale filozofia uniksowa, jeśli chodzi o I / O, to „wszystko jest plikiem”. W praktyce, to naprawdę oznacza, że można wykorzystać te same funkcje i interfejsy (biblioteka printf, scanf, read, write, select, itd.), Nie martwiąc się o to, czy strumień I / O jest podłączony do klawiatury, plik na dysku, gniazda, rury, lub inna abstrakcja we / wy.

Większość programów trzeba czytać wejścia, wyjścia i błędów zapisu dziennika, tak stdin, stdouti stderrsą predefiniowane dla ciebie, dla wygody programowania. Jest to tylko konwencja i nie jest egzekwowana przez system operacyjny.

Jim Lewis
źródło
Dziękuję za twoje uwagi. Czy wiesz, jak mógłbym przechwycić wyjściowy strumień danych procesu i wyprowadzić go do własnego pliku?
Shouvik,
51

Jako uzupełnienie powyższych odpowiedzi, oto podsumowanie dotyczące przekierowań: Arkusz przekierowań

EDYCJA: Ta grafika nie jest całkowicie poprawna, ale nie jestem pewien, dlaczego ...

Grafika mówi, że 2> i 1 ma taki sam efekt jak &>

ls Documents ABC > dirlist 2>&1
#does not give the same output as 
ls Documents ABC > dirlist &>
Leopold Gault
źródło
4
Twój komentarz w połączeniu z zaakceptowaną odpowiedzią ma doskonały sens i jasno wyjaśnia rzeczy! Dzięki!
Mykola
1
Obraz jest wart tysiąca słów !
tauseef_CuriousGuy
22

Obawiam się, że twoje zrozumienie jest całkowicie wstecz. :)

Pomyśl o „standardowym wejściu”, „standardowym wyjściu” i „standardowym błędzie” z perspektywy programu , a nie z perspektywy jądra.

Gdy program musi wydrukować dane wyjściowe, zwykle drukuje na „standardowe wyjście”. Program zwykle drukuje dane wyjściowe na standardowe wyjście, przy printfczym drukuje TYLKO na standardowe wyjście.

Gdy program musi wydrukować informacje o błędzie (niekoniecznie wyjątki, są to konstrukcje języka programowania, narzucone na znacznie wyższym poziomie), zwykle drukuje na „błędzie standardowym”. Zwykle robi to za pomocą fprintf, który akceptuje strumień plików do użycia podczas drukowania. Strumieniem plików może być dowolny plik otwarty do zapisu: standardowe wyjście, błąd standardowy lub dowolny inny plik otwarty za pomocą fopenlub fdopen.

„standardowe wejście” jest używane, gdy plik musi odczytać dane wejściowe, używając freadlub fgets, lub getchar.

Każdy z tych plików można łatwo przekierować z powłoki, tak jak to:

cat /etc/passwd > /tmp/out     # redirect cat's standard out to /tmp/foo
cat /nonexistant 2> /tmp/err   # redirect cat's standard error to /tmp/error
cat < /etc/passwd              # redirect cat's standard input to /etc/passwd

Lub cała enchilada:

cat < /etc/passwd > /tmp/out 2> /tmp/err

Istnieją dwa ważne zastrzeżenia: po pierwsze, „standardowe wejście”, „standardowe wyjście” i „standardowy błąd” to tylko konwencja. Są to bardzo mocna konwencja, ale wszystko jest tylko zgodą, że bardzo miło jest móc uruchamiać programy w ten sposób: grep echo /etc/services | awk '{print $2;}' | sorti mieć standardowe wyjścia każdego programu podłączone do standardowego wejścia następnego programu w potoku.

Po drugie, podałem standardowe funkcje ISO C do pracy ze strumieniami plików ( FILE *obiektami) - na poziomie jądra są to wszystkie deskryptory plików ( intodniesienia do tabeli plików) oraz operacje na niższych poziomach, takie jak readi write, które nie wykonaj szczęśliwe buforowanie funkcji ISO C. Pomyślałem, że będę prostszy i używał łatwiejszych funkcji, ale pomyślałem, że mimo wszystko powinieneś znać alternatywy. :)

Sarnold
źródło
Tak samo jest, gdy proces jest wykonywany, zapisuje błędy w tym pliku stderr lub gdy program jest kompilowany ze źródła. Także kiedy mówimy o tych plikach z perspektywy kompilatora, czy jest inaczej niż w przypadku porównania z powiedzmy programem?
Shouvik,
1
@Shouvik, kompilator jest po prostu kolejnym programem z własnymi stdin, stdout i stderr. Kiedy kompilator musi zapisać ostrzeżenia lub błędy, zapisze je na stderr. Gdy front-end kompilatora wyprowadza kod pośredni dla asemblera, może on napisać kod pośredni na standardowym wyjściu, a asembler może zaakceptować jego wejście na standardowym wyjściu, ale wszystko to będzie za kulisami z perspektywy użytkownika.) skompilowany program, program ten może także zapisywać błędy w swoich błędach standardowych, ale nie ma to nic wspólnego z kompilacją.
sarnold
Dzięki za ten token informacji.
Wydaje
1
Mówisz więc, że standard pomaga nam wydrukować program
babygame0ver
9

standardowe

Czyta dane wejściowe przez konsolę (np. Klawiatura). Używany w C z scanf

scanf(<formatstring>,<pointer to storage> ...);

standardowe

Generuje dane wyjściowe do konsoli. Używany w C z printf

printf(<string>, <values to print> ...);

stderr

Generuje wyjście „błędu” do konsoli. Używany w C z fprintf

fprintf(stderr, <string>, <values to print> ...);

Przekierowanie

Źródło stdin można przekierować. Na przykład, zamiast pochodzić z klawiatury, może pochodzić z pliku ( echo < file.txt) lub innego programu ( ps | grep <userid>).

Miejsca docelowe dla stdout, stderr można również przekierować. Na przykład stdout może zostać przekierowany do pliku: ls . > ls-output.txtw tym przypadku dane wyjściowe są zapisywane w pliku ls-output.txt. Stderr można przekierować za pomocą 2>.

mikek3332002
źródło
8

Myślę, że ludzie mówią, że stderrpowinny być używane tylko do komunikatów o błędach, są mylące.

Powinien być również używany do komunikatów informacyjnych, które są przeznaczone dla użytkownika uruchamiającego polecenie, a nie dla potencjalnych dalszych użytkowników danych (tj. Jeśli uruchomisz potok powłoki łączący kilka poleceń, nie chcesz komunikatów informacyjnych, takich jak „pobranie pozycji 30 z 42424 ”, aby pojawił się na stdoutekranie, ponieważ wprowadzą konsumenta w błąd, ale nadal możesz chcieć, aby użytkownik je zobaczył.

Zobacz to dla uzasadnienia historycznego:

„Wszystkie programy umieszczały diagnostykę na standardowym wyjściu. Zawsze powodowało to problemy, gdy dane wyjściowe były przekierowywane do pliku, ale stawały się nie do zniesienia, gdy dane wyjściowe były wysyłane do niczego niepodejrzewającego procesu. Niemniej jednak, nie chcąc naruszać prostoty standardowego wejścia - model o standardowym wyjściu, ludzie tolerowali ten stan rzeczy do wersji 6. Wkrótce potem Dennis Ritchie przeciął węzeł gordyjski, wprowadzając standardowy plik błędów. To nie wystarczyło. W przypadku rurociągów diagnostyka może pochodzić z dowolnego z kilku programów działających jednocześnie. Potrzebna diagnostyka identyfikować się ”.

dee
źródło
3

Użycie ps -aux ujawnia bieżące procesy, z których wszystkie są wymienione w / proc / as / proc / (pid) /, wywołując cat / proc / (pid) / fd / 0 wypisuje wszystko, co znajduje się na standardowym wyjściu myślę, że ten proces. Więc może,

/ proc / (pid) / fd / 0 - Standardowy plik wyjściowy
/ proc / (pid) / fd / 1 - Standardowy plik wejściowy
/ proc / (pid) / fd / 2 - Standardowy plik błędu

na przykładmoje okno terminala

Ale działało to dobrze tylko w przypadku / bin / bash, inne procesy na ogół nie miały nic w 0, ale wiele z nich miało błędy zapisane w 2

Sam
źródło
3

Aby uzyskać wiarygodne informacje o tych plikach, sprawdź strony podręcznika man, uruchom polecenie na swoim terminalu.

$ man stdout 

Ale dla prostej odpowiedzi każdy plik służy do:

standardowe wyjście dla strumienia wyjściowego

standardowe wejście strumienia

stderr do drukowania błędów lub komunikatów w dzienniku.

Każdy program uniksowy ma każdy z tych strumieni.

Margach Chris
źródło
2

stderr nie będzie buforował pamięci podręcznej IO, więc jeśli nasza aplikacja będzie musiała wydrukować krytyczne informacje o wiadomościach (niektóre błędy, wyjątki) na konsolę lub do pliku, użyj go, gdy używasz standardowego wyjścia do drukowania ogólnych informacji dziennika, ponieważ używa buforowania IO Cache, istnieje szansa, że przed zapisaniem naszych wiadomości do aplikacji plikowej może się zamknąć, pozostawiając skomplikowane debugowanie

geekanil
źródło
0

Plik z powiązanym buforowaniem nazywany jest strumieniem i deklarowany jako wskaźnik do zdefiniowanego typu PLIK. Funkcja fopen () tworzy pewne dane opisowe dla strumienia i zwraca wskaźnik do oznaczenia strumienia we wszystkich dalszych transakcjach. Zwykle istnieją trzy otwarte strumienie ze stałymi wskaźnikami zadeklarowanymi w nagłówku i powiązanymi ze standardowymi otwartymi plikami. Podczas uruchamiania programu trzy strumienie są wstępnie zdefiniowane i nie muszą być jawnie otwierane: standardowe wejście (do odczytu konwencjonalnego wejścia), standardowe wyjście (do zapisu konwencjonalnego wyjścia) i standardowy błąd (do zapisu wyjścia diagnostycznego). Po otwarciu standardowy strumień błędów nie jest w pełni buforowany; standardowe strumienie wejściowe i wyjściowe są w pełni buforowane wtedy i tylko wtedy, gdy można ustalić, że strumień nie odnosi się do urządzenia interaktywnego

https://www.mkssoftware.com/docs/man5/stdio.5.asp

Bahruz Balabayov
źródło