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

Photo of author

By maciekx

Strumienie stdin, stdout i stderr to trzy strumienie danych, które są tworzone podczas uruchamiania polecenia w systemie Linux. Można je wykorzystać do ustalenia, czy skrypty są przesyłane potokiem lub przekierowywane. W tym artykule pokażemy, jak to zrobić.

Strumienie łączą dwa punkty

Podczas nauki o systemach operacyjnych Linux i uniksopodobnych często napotykasz na terminy stdin, stdout i stderr. To są trzy standardowe strumienie, które są ustalane w momencie wykonywania polecenia w systemie Linux. W informatyce strumień to coś, co może przesyłać dane, a w przypadku tych strumieni danymi jest tekst.

Strumienie danych, podobnie jak strumienie wody, mają dwa końce: źródło i odpływ. Każde polecenie w systemie Linux zapewnia jeden koniec każdego strumienia, podczas gdy drugi koniec jest określany przez powłokę, która uruchomiła polecenie. Ten koniec zostaje podłączony do okna terminala, potoku lub przekierowany do pliku lub innego polecenia, zgodnie z wierszem poleceń, które zostało użyte.

Strumienie w standardzie Linux

W Linuksie stdin to standardowy strumień wejściowy, który akceptuje tekst jako dane wejściowe. Wyjście tekstowe polecenia do powłoki jest dostarczane przez strumień stdout (standardowe wyjście), a komunikaty o błędach są wysyłane przez strumień stderr (standardowy błąd).

W ten sposób istnieją dwa strumienie wyjściowe: stdout i stderr oraz jeden strumień wejściowy: stdin. Komunikaty o błędach i normalne dane wyjściowe mają własny kanał, co umożliwia ich niezależne przetwarzanie.

Strumienie są obsługiwane jak pliki

Strumienie w Linuksie – podobnie jak wiele innych elementów – są traktowane jak pliki. Można czytać tekst z pliku i zapisywać tekst do pliku, a obie te czynności dotyczą strumienia danych. Koncepcja traktowania strumienia danych jako pliku nie jest zatem zbyt trudna do zrozumienia.

Każdy plik skojarzony z procesem ma przypisany unikalny numer, nazywany deskryptorem pliku. Deskryptor pliku identyfikuje plik i jest używany, gdy wymagane jest wykonanie operacji na pliku.

Standardowe wartości dla stdin, stdout i stderr to:

  • 0: stdin
  • 1: stdout
  • 2: stderr

Reagowanie na potoki i przekierowania

Aby wprowadzić kogoś w temat, powszechną techniką jest nauczanie uproszczonej wersji tematu. Na przykład w gramatyce mówi się, że reguła brzmi „I przed E, z wyjątkiem po C”. W rzeczywistości jednak jest więcej wyjątków od tej reguły niż przypadków, które ją przestrzegają.

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

Można łatwo zauważyć tę zmianę w zachowaniu. Wypróbuj poniższe polecenia:

ls

ls | cat

Polecenie ls zachowuje się inaczej, gdy jego wyjście (stdout) jest przesyłane potokiem do innego polecenia. W tym przypadku ls przełącza się na wyjście w pojedynczej kolumnie, co nie jest konwersją wykonywaną przez cat. Podobnie ls działa w ten sam sposób, gdy jego wyjście jest przekierowywane:

ls > capture.txt

cat capture.txt

Przekierowywanie stdout i stderr

Istnieje korzyść z tego, ż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. Dzięki temu możemy na nie reagować w razie potrzeby, co zapobiega zanieczyszczeniu pliku, do którego przekierowywane jest standardowe wyjście.

Wpisz następujący kod w edytorze i zapisz go jako plik error.sh:

#!/bin/bash
echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

Uczyń skrypt wykonywalnym za pomocą poniższego polecenia:

chmod +x error.sh

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

Uruchom skrypt za pomocą poniższego polecenia:

./error.sh

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

Spróbujmy przekierować wyjście do pliku:

./error.sh > capture.txt

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 zostało przekierowane:

cat capture.txt

Przekierowywanie zarówno stdout, jak i stderr

Możemy przekierować stdout lub stderr, ale czy możemy przekierować je oba 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

W związku z tym, ponieważ oba strumienie wyjścia – standardowe wyjście i standardowy błąd – są przekierowywane do plików, nie ma widocznych wyników w oknie terminala. Wracamy do wiersza poleceń, jakby nic się nie stało.

Przekierowywanie stdout i stderr do tego samego pliku

Możemy także przekierować 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.
  • 2>&1: pozwala powiedzieć powłoce, aby strumień stderr dotarł do tego samego miejsca przeznaczenia, co strumień stdout.

Nie będzie widocznych wyników. Sprawdźmy zawartość pliku capture.txt, aby zobaczyć, co w nim jest:

cat capture.txt

Wykrywanie przekierowań w skrypcie

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

Wpisz następujący kod 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 poniższego polecenia, aby uczynić go wykonywalnym:

chmod +x input.sh

Test w nawiasach kwadratowych wykorzystuje opcję -t (terminal), która zwraca prawdę (0), jeśli plik powiązany z deskryptorem pliku kończy się w oknie terminala. Używamy deskryptora pliku 0 jako argumentu testu, który reprezentuje stdin.

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

Możemy użyć dowolnego pliku tekstowego jako źródła danych wejściowych do skryptu. W tym przypadku użyjemy pliku o nazwie dummy.txt:

./input.sh < dummy.txt

Wyjście pokazuje, że skrypt rozpoznaje, że dane wejściowe nie pochodzą z klawiatury, lecz z pliku.

Teraz spróbujmy z potokiem:

cat dummy.txt | ./input.sh

Skrypt rozpoznaje, że jego dane wejściowe są przesyłane potokiem i odpowiednio to zgłasza.

Uruchommy skrypt bez potoków ani przekierowań:

./input.sh

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

Strumienie świadomości

Wiedza na temat tego, jak stwierdzić, czy skrypty są podłączone do okna terminala, potoku lub są przekierowywane, pozwala 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.


newsblog.pl