Co to są stdin, stdout i stderr w systemie Linux?

stdin, stdout i stderr to trzy strumienie danych tworzone podczas uruchamiania polecenia systemu Linux. Możesz ich użyć, aby stwierdzić, czy Twoje skrypty są przesyłane potokiem lub przekierowywane. Pokażemy Ci, jak to zrobić.

Strumienie łączą dwa punkty

Gdy tylko zaczniesz uczyć się o systemach operacyjnych Linux i uniksopodobnych, napotkasz terminy stdin, stdout i stederr. To są trzy standardowe strumienie które są ustalane podczas wykonywania polecenia systemu Linux. W informatyce strumień to coś, co może przesyłać dane. W przypadku tych strumieni danymi jest tekst.

Strumienie danych, podobnie jak strumienie wody, mają dwa końce. Mają źródło i odpływ. Jakiekolwiek polecenie Linuksa, którego używasz, zapewnia jeden koniec każdego strumienia. Drugi koniec jest określany przez powłokę, która uruchomiła polecenie. Ten koniec zostanie podłączony do okna terminala, podłączony do potoku lub przekierowany do pliku lub innego polecenia, zgodnie z wierszem poleceń, które uruchomiło polecenie.

Strumienie w standardzie Linux

W Linuksie stdin jest standardowym strumieniem wejściowym. Akceptuje tekst jako dane wejściowe. Wyjście tekstowe polecenia do powłoki jest dostarczane przez strumień stdout (standardowe wyjście). Komunikaty o błędach z polecenia są wysyłane przez strumień stderr (standardowy błąd).

Możesz więc zobaczyć, że istnieją dwa strumienie wyjściowe, stdout i stderr oraz jeden strumień wejściowy, stdin. Ponieważ komunikaty o błędach i normalne dane wyjściowe mają własny kanał przenoszący je do okna terminala, mogą być obsługiwane niezależnie od siebie.

Strumienie są obsługiwane jak pliki

Strumienie w Linuksie – jak prawie wszystko inne – są traktowane tak, jakby były plikami. Możesz czytać tekst z pliku i zapisywać tekst do pliku. Obie te czynności obejmują strumień danych. Więc koncepcja obsługi strumienia danych jako pliku nie jest zbyt rozciągliwa.

Każdy plik skojarzony z procesem ma przypisany unikalny numer, który go identyfikuje. Nazywa się to deskryptorem pliku. Zawsze, gdy wymagane jest wykonanie działania na pliku, deskryptor pliku służy do identyfikacji pliku.

Te wartości są zawsze używane dla stdin, stdout i stderr:

0: stdin
1: standardowe wyjście
2: stderr

Reagowanie na potoki i przekierowania

Aby ułatwić komuś wprowadzenie do tematu, powszechną techniką jest nauczanie uproszczonej wersji tematu. Na przykład w przypadku gramatyki mówi się nam, że reguła brzmi „I przed E, z wyjątkiem po C”. Ale tak naprawdę tam jest więcej wyjątków od tej reguły niż są przypadki, które go przestrzegają.

W podobny sposób, mówiąc o stdin, stdout i stderr, wygodnie jest wyciągnąć akceptowany aksjomat, że proces nie wie ani nie dba o to, gdzie kończą się jego trzy standardowe strumienie. Czy proces powinien dbać o to, czy jego dane wyjściowe trafiają na terminal, czy też są przekierowywane do pliku? Czy może nawet stwierdzić, czy jego dane wejściowe pochodzą z klawiatury, czy są przesyłane do niego z innego procesu?

W rzeczywistości proces wie – lub przynajmniej może się dowiedzieć, gdyby zdecydował się sprawdzić – i może odpowiednio zmienić swoje zachowanie, jeśli autor oprogramowania zdecyduje się dodać tę funkcję.

Bardzo łatwo możemy zauważyć tę zmianę w zachowaniu. Wypróbuj te dwa polecenia:

ls

Znajduje się w oknie terminala

ls | cat

ls w oknie terminala

Polecenie ls zachowuje się inaczej, jeśli jego wyjście (stdout) jest przesyłane potokiem do innego polecenia. To ls przełącza się na wyjście pojedynczej kolumny, nie jest to konwersja wykonywana przez cat. I ls robi to samo, jeśli jego wyjście jest przekierowywane:

ls > capture.txt

ls> capture.txt w oknie terminala ”width =” 646 ″ height = ”57 ″ onload =” pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this); ”  onerror = ”this.onerror = null; pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this);”> </p>
<pre> cat capture.txt </pre>
<p> <img loading =

Przekierowywanie stdout i stderr

Istnieje zaleta, że ​​komunikaty o błędach są dostarczane przez dedykowany strumień. Oznacza to, że możemy przekierować dane wyjściowe polecenia (stdout) do pliku i nadal widzieć wszelkie komunikaty o błędach (stderr) w oknie terminala. W razie potrzeby możesz zareagować na pojawiające się błędy. Powstrzymuje również komunikaty o błędach przed zanieczyszczeniem pliku, do którego zostało przekierowane standardowe wyjście.

Wpisz następujący tekst w edytorze i zapisz go w pliku o nazwie error.sh.

#!/bin/bash

echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

Spraw, aby skrypt był wykonywalny za pomocą tego polecenia:

chmod +x error.sh

Pierwsza linia skryptu odsyła tekst do okna terminala za pośrednictwem strumienia standardowego. Druga linia próbuje uzyskać dostęp do pliku, który nie istnieje. Spowoduje to wygenerowanie komunikatu o błędzie, który jest dostarczany przez stderr.

Uruchom skrypt za pomocą tego polecenia:

./error.sh

./error.sh w oknie terminala

Widzimy, że oba strumienie wyjścia, stdout i stderr, zostały wyświetlone w oknach terminala.

wyjście ze skryptu error.sh w oknie terminala

Spróbujmy przekierować wyjście do pliku:

./error.sh > capture.txt

./error.sh> capture.txt w oknie terminala ”width =” 646 ″ height = ”57 ″ onload =” pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this); ”  onerror = ”this.onerror = null; pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this);”> </p>
<p> Komunikat o błędzie dostarczany przez stderr jest nadal wysyłany do okna terminala.  Możemy sprawdzić zawartość pliku, aby zobaczyć, czy wyjście standardowe trafiło do pliku. </p>
<pre> cat capture.txt </pre>
<p> <img loading =

Dane wyjściowe ze stdin zostały przekierowane do pliku zgodnie z oczekiwaniami.

zawartość pliku capture.txt w oknie terminala

Symbol przekierowania> domyślnie działa z wyjściem standardowym. Możesz użyć jednego z numerycznych deskryptorów plików, aby wskazać, który standardowy strumień wyjściowy chcesz przekierować.

Aby jawnie przekierować stdout, użyj tej instrukcji przekierowania:

1>

Aby jawnie przekierować stderr, użyj tej instrukcji przekierowania:

2>

Spróbujmy jeszcze raz nasz test, tym razem użyjemy 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt w oknie terminala ”width =” 646 ″ height = ”57 ″ onload =” pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this); ”  onerror = ”this.onerror = null; pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this);”> </p>
<p> Komunikat o błędzie jest przekierowywany, a standardowe echo jest wysyłane do okna terminala: </p>
<p > <img class =

Zgodnie z oczekiwaniami, komunikat stderr znajduje się w pliku capture.txt.

zawartość pliku capture.txt w oknie terminala

Przekierowywanie zarówno stdout, jak i stderr

Z pewnością, jeśli możemy przekierować stdout lub stderr do pliku niezależnie od siebie, powinniśmy mieć możliwość przekierowania ich obu jednocześnie, do dwóch różnych plików?

Tak możemy. To polecenie skieruje standardowe wyjście do pliku o nazwie capture.txt, a stderr do pliku o nazwie error.txt.

./error.sh 1> capture.txt 2> error.txt

./error.sh 1> capture.txt 2> error.txt w oknie terminala ”width =” 646 ″ height = ”57 ″ onload =” pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this); ”  onerror = ”this.onerror = null; pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this);”> </p>
<p> Ponieważ oba strumienie wyjścia – standardowe wyjście i standardowy błąd – są przekierowywane do plików, nie ma widocznych wyników w okno terminala.  Wracamy do wiersza poleceń, jakby nic się nie stało. </p>
<p> <img loading =

Sprawdźmy zawartość każdego pliku:

cat capture.txt
cat error.txt

zawartość plików capture.txt i error.txt w oknie terminala

Przekierowywanie stdout i stderr do tego samego pliku

To fajne, każdy ze standardowych strumieni wyjściowych trafia do własnego dedykowanego pliku. Jedyną inną kombinacją, jaką możemy zrobić, jest wysłanie zarówno stdout, jak i stderr do tego samego pliku.

Możemy to osiągnąć za pomocą następującego polecenia:

./error.sh > capture.txt 2>&1

Rozbijmy to.

./error.sh: uruchamia plik skryptowy error.sh.
> capture.txt: przekierowuje strumień stdout do pliku capture.txt. > jest skrótem od 1>.
2> & 1: używa instrukcji przekierowania &>. Ta instrukcja pozwala powiedzieć powłoce, aby jeden strumień dotarł do tego samego miejsca przeznaczenia, co inny strumień. W tym przypadku mówimy „przekieruj strumień 2, stderr, do tego samego miejsca docelowego, do którego jest przekierowywany strumień 1, stdout”.

./error.sh> capture.txt 2 &> 1 w oknie terminala ”width =” 646 ″ height = ”57 ″ onload =” pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this); ”  onerror = ”this.onerror = null; pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon (this);”> </p>
<p> Brak widocznych wyników.  To zachęcające. </p>
<p> <img loading =

Sprawdźmy plik capture.txt i zobaczmy, co w nim jest.

cat capture.txt

zawartość pliku capture.txt w oknie terminala

Strumienie stdout i stderr zostały przekierowane do pojedynczego pliku docelowego.

Aby dane wyjściowe strumienia zostały przekierowane i po cichu wyrzucone, przekieruj dane wyjściowe do / dev / null.

Wykrywanie przekierowań w skrypcie

Omówiliśmy, w jaki sposób polecenie może wykryć, czy którykolwiek ze strumieni jest przekierowywany, i może odpowiednio zmienić jego zachowanie. Czy możemy to osiągnąć w naszych własnych skryptach? Tak możemy. Jest to bardzo łatwa do zrozumienia i zastosowania technika.

Wpisz następujący tekst w edytorze i zapisz go jako input.sh.

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

Użyj następującego polecenia, aby uczynić go wykonywalnym:

chmod +x input.sh

Sprytną częścią jest test w nawiasach kwadratowych. Opcja -t (terminal) zwraca prawdę (0), jeśli plik powiązany z deskryptorem pliku kończy się w oknie terminala. Użyliśmy deskryptora pliku 0 jako argumentu testu, który reprezentuje stdin.

Jeśli stdin jest podłączony do okna terminala, test okaże się prawdziwy. Jeśli stdin jest podłączone do pliku lub potoku, test zakończy się niepowodzeniem.

Możemy użyć dowolnego wygodnego pliku tekstowego do wygenerowania danych wejściowych do skryptu. Tutaj używamy jednego o nazwie dummy.txt.

./input.sh 

./input.sh <dummy.txt w oknie terminala

The output shows that the script recognizes that the input isn’t coming from a keyboard, it is coming from a file. If you chose to, you could vary your script’s behavior accordingly.

wyjście ze skryptu w oknie terminala

To było z przekierowaniem pliku, spróbujmy z potokiem.

cat dummy.txt |  ./input.sh

cat dummy.txt |  ./input.sh w oknie terminala

Skrypt rozpoznaje, że jego dane wejściowe są do niego przesyłane potokiem. A dokładniej, ponownie rozpoznaje, że strumień stdin nie jest podłączony do okna terminala.

wyjście skryptu w oknie terminala

Uruchommy skrypt bez potoków ani przekierowań.

./input.sh

./input.sh w oknie terminala

Strumień stdin jest podłączony do okna terminala, a skrypt odpowiednio to zgłasza.

Aby sprawdzić to samo ze strumieniem wyjściowym, potrzebujemy nowego skryptu. Wpisz następujące polecenie do edytora i zapisz jako output.sh.

#!/bin/bash

if [ -t 1 ]; then

echo stdout is going to the terminal window
 
else

echo stdout is being redirected or piped
 
fi

Użyj następującego polecenia, aby uczynić go wykonywalnym:

chmod +x input.sh

Jedyna znacząca zmiana w tym skrypcie dotyczy testu w nawiasach kwadratowych. Używamy cyfry 1 do reprezentowania deskryptora pliku dla standardowego wyjścia.

Wypróbujmy to. Przekażemy wyjście przez cat.

./output | cat

./output |  kot w oknie terminala

Skrypt rozpoznaje, że jego dane wyjściowe nie są kierowane bezpośrednio do okna terminala.

wyjście skryptu w oknie terminala

Możemy również przetestować skrypt, przekierowując dane wyjściowe do pliku.

./output.sh > capture.txt

./output.sh> capture.txt w oknie terminala

Nie ma wyjścia w oknie terminala, po cichu wracamy do wiersza poleceń. Jak się spodziewaliśmy.

wyjście skryptu w oknie terminala

Możemy zajrzeć do pliku capture.txt, aby zobaczyć, co zostało przechwycone. Aby to zrobić, użyj następującego polecenia.

cat capture.sh

cat capture.sh w oknie terminala

Ponownie, prosty test w naszym skrypcie wykrywa, że ​​strumień stdout nie jest wysyłany bezpośrednio do okna terminala.

Jeśli uruchomimy skrypt bez potoków lub przekierowań, powinien on wykryć, że standardowe wyjście jest dostarczane bezpośrednio do okna terminala.

./output.sh

./output.sh w oknie terminala

I to jest dokładnie to, co widzimy.

wyjście skryptu w oknie terminala

Strumienie świadomości

Wiedza o tym, jak stwierdzić, czy skrypty są podłączone do okna terminala, potoku lub są przekierowywane, pozwala odpowiednio dostosować ich zachowanie.

Rejestrowanie i dane diagnostyczne mogą być mniej lub bardziej szczegółowe, w zależności od tego, czy mają być wyświetlane na ekranie, czy do pliku. Komunikaty o błędach mogą być rejestrowane w innym pliku niż normalne dane wyjściowe programu.

Jak to zwykle bywa, większa wiedza daje więcej możliwości.