stdin
, stdout
i stderr
są strumieniami dołączonymi odpowiednio do deskryptorów plików 0, 1 i 2 procesu.
Po zachęcie interaktywnej powłoki w terminalu lub emulatorze terminali wszystkie 3 deskryptory plików będą odnosić się do tego samego opisu otwartego pliku, który zostałby uzyskany przez otwarcie pliku urządzenia terminalowego lub pseudoterminalowego (coś podobnego /dev/pts/0
) w trybie odczytu i zapisu tryb.
Jeśli z tej interaktywnej powłoki uruchomisz skrypt bez użycia przekierowania, skrypt odziedziczy deskryptory plików.
W systemie Linux /dev/stdin
, /dev/stdout
, /dev/stderr
są symboliczne linki do /proc/self/fd/0
, /proc/self/fd/1
, /proc/self/fd/2
odpowiednio, sami specjalne dowiązania do rzeczywistego pliku, który jest otwarty na tych deskryptorów.
Nie są to stdin, stdout, stderr, są to specjalne pliki, które identyfikują pliki, do których idą stdin, stdout, stderr (zauważ, że jest inny w innych systemach niż Linux, które mają te specjalne pliki).
odczytanie czegoś ze standardowego oznacza odczyt z deskryptora pliku 0 (który wskaże gdzieś w pliku, do którego odwołuje się /dev/stdin
).
Ale $(</dev/stdin)
wewnątrz powłoka nie odczytuje ze standardowego wejścia, otwiera nowy deskryptor pliku do odczytu na tym samym pliku, co ten otwarty na standardowym wejściu (więc czytanie od początku pliku, a nie tam, gdzie aktualnie wskazuje standardowe wejście).
Z wyjątkiem szczególnego przypadku urządzeń końcowych otwartych w trybie odczytu i zapisu, stdout i stderr zwykle nie są otwarte do odczytu. Mają być strumieniami, do których piszesz . Czytanie z deskryptora pliku 1 zazwyczaj nie będzie działać. W systemie Linux otwieranie /dev/stdout
lub /dev/stderr
czytanie (jak w $(</dev/stdout)
) działałoby i pozwalało ci czytać z pliku, do którego przechodzi stdout (a jeśli stdout był potokiem, to czytałby z drugiego końca potoku, a gdyby był gniazdem , to się nie powiedzie, ponieważ nie można otworzyć gniazda).
W naszym przypadku skryptu uruchamianego bez przekierowania po zachęcie interaktywnej powłoki w terminalu, wszystkie / dev / stdin, / dev / stdout i / dev / stderr będą plikami / dev / pts / x terminal device.
Odczyt z tych plików specjalnych zwraca to, co jest wysyłane przez terminal (to, co wpisujesz na klawiaturze). Zapis do nich wyśle tekst do terminala (do wyświetlenia).
echo $(</dev/stdin)
echo $(</dev/stderr)
będzie taki sam. Aby rozwinąć $(</dev/stdin)
, powłoka otworzy / dev / pts / 0 i będzie czytać to, co piszesz, dopóki nie naciśniesz ^D
pustej linii. Następnie przekażą rozwinięcie (to, co wpisałeś, pozbawione końcowych znaków nowej linii i podlegają podziałowi + globowi), do echo
którego następnie wyprowadzą je na standardowe wyjście (do wyświetlenia).
Jednak w:
echo $(</dev/stdout)
w bash
( i bash
tylko ) ważne jest, aby zdać sobie sprawę, że w środku $(...)
przekierowano stdout. To jest teraz fajka. W przypadku procesu bash
potomnego proces odczytywania zawartości pliku (tutaj /dev/stdout
) i zapisywania go do potoku, podczas gdy rodzic czyta z drugiego końca, aby utworzyć rozszerzenie.
W tym przypadku, gdy proces potomnego bashu się otworzy /dev/stdout
, faktycznie otwiera on koniec odczytu potoku. Nic z tego nigdy nie wyjdzie, to sytuacja impasu.
Jeśli chcesz czytać z pliku wskazywanego przez standardowe skrypty, możesz obejść to za pomocą:
{ echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1
To by powieliło fd 1 na fd 3, więc / dev / fd / 3 wskazywałoby na ten sam plik co / dev / stdout.
Skryptem takim jak:
#! /bin/bash -
printf 'content of file on stdin: %s\n' "$(</dev/stdin)"
{ printf 'content of file on stdout: %s\n' "$(</dev/fd/3)"; } 3<&1
printf 'content of file on stderr: %s\n' "$(</dev/stderr)"
Uruchamiany jako:
echo bar > err
echo foo | myscript > out 2>> err
Zobaczysz out
później:
content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
Jeśli w przeciwieństwie do odczytu z /dev/stdin
, /dev/stdout
, /dev/stderr
, chciałeś odczytać z stdin, stdout i stderr (co spowodowałoby jeszcze mniej sensu), można zrobić:
#! /bin/sh -
printf 'what I read from stdin: %s\n' "$(cat)"
{ printf 'what I read from stdout: %s\n' "$(cat <&3)"; } 3<&1
printf 'what I read from stderr: %s\n' "$(cat <&2)"
Jeśli ponownie uruchomiłeś ten drugi skrypt jako:
echo bar > err
echo foo | myscript > out 2>> err
Zobaczysz w out
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr:
oraz w err
:
bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor
W przypadku stdout i stderr cat
kończy się niepowodzeniem, ponieważ deskryptory plików były otwarte tylko do zapisu , a nie do odczytu, rozszerzenie $(cat <&3)
i $(cat <&2)
jest puste.
Jeśli nazwałeś to jako:
echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err
(gdzie <>
otwiera się w trybie odczytu + zapisu bez obcinania), zobaczysz w out
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
oraz w err
:
err
Zauważysz, że nic nie zostało odczytane z stdout, ponieważ poprzedni printf
miał nadpisane zawartości out
z what I read from stdin: foo\n
i opuścił stanowisko stdout w tym pliku tuż po. Jeśli przygotowałeś out
większy tekst, na przykład:
echo 'This is longer than "what I read from stdin": foo' > out
Wtedy dostaniesz się out
:
what I read from stdin: foo
read from stdin": foo
what I read from stdout: read from stdin": foo
what I read from stderr: err
Zobacz, w jaki sposób $(cat <&3)
przeczytał to, co zostało po pierwszym, printf
i przesunął o to pozycję stdout obok niego, tak aby następne dane printf
wyjściowe zostały odczytane po.
echo x
to nie to samo,echo x > /dev/stdout
że stdout nie przechodzi do potoku lub niektórych urządzeń znakowych, takich jak tty. Na przykład, jeśli stdout przejdzie do zwykłego plikuecho x > /dev/stdout
, skróci plik i zastąpi jego zawartośćx\n
zamiast piszącx\n
w bieżącej pozycji stdout.echo this is your error $(cat /dev/stderr)
?stdout
istderr
są wyjściami.myscript.sh:
Następnie uruchomić:
Prawdopodobnie trzeba będzie zainstalować
filan
zsudo apt install socat
pierwszym.źródło