echo lub print / dev / stdin / dev / stdout / dev / stderr


14

Chcę wydrukować wartość / dev / stdin, / dev / stdout i / dev / stderr.

Oto mój prosty skrypt:

#!/bin/bash
echo your stdin is : $(</dev/stdin)
echo your stdout is : $(</dev/stdout)
echo your stderr is : $(</dev/stderr)

używam następujących rur:

[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh

tylko $(</dev/stdin)wydaje się działać, znalazłem też na inne pytania, z których korzystali ludzie: "${1-/dev/stdin}"wypróbowałem to bez powodzenia.

Odpowiedzi:


30

stdin, stdouti stderrstrumieniami 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/stderrsą symboliczne linki do /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2odpowiednio, 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/stdoutlub /dev/stderrczytanie (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 ^Dpustej linii. Następnie przekażą rozwinięcie (to, co wpisałeś, pozbawione końcowych znaków nowej linii i podlegają podziałowi + globowi), do echoktórego następnie wyprowadzą je na standardowe wyjście (do wyświetlenia).

Jednak w:

echo $(</dev/stdout)

w bash( i bashtylko ) ważne jest, aby zdać sobie sprawę, że w środku $(...)przekierowano stdout. To jest teraz fajka. W przypadku procesu bashpotomnego 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 outpóź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 catkoń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 printfmiał nadpisane zawartości outz what I read from stdin: foo\ni opuścił stanowisko stdout w tym pliku tuż po. Jeśli przygotowałeś outwię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 printfwyjściowe zostały odczytane po.


2

stdouti stderrsą wyjściami, nie czytasz z nich, możesz tylko do nich pisać. Na przykład:

echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr

programy domyślnie zapisują na standardowe wyjście, więc pierwszy jest równoważny

echo "this is stdout"

i możesz przekierować stderr na inne sposoby, takie jak

echo "this is stderr" 1>&2

1
W Linuksie echo xto 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 pliku echo x > /dev/stdout, skróci plik i zastąpi jego zawartość x\nzamiast pisząc x\nw bieżącej pozycji stdout.
Stéphane Chazelas

@ Michael-Daffin> Dziękuję, więc jeśli chcę przeczytać stdout i stderr, mogę używać tylko kota? (ponieważ są to pliki), na przykład chcę pokazać użytkownikowi ostatni występujący błąd echo this is your error $(cat /dev/stderr)?
Omar BISTAMI

@OmarBISTAMI Nie możesz czytać stdouti stderrsą wyjściami.
Kusalananda

1

myscript.sh:

#!/bin/bash
filan -s

Następnie uruchomić:

$ ./myscript.sh
    0 tty /dev/pts/1
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ ls | ./myscript.sh
    0 pipe 
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ ls | ./myscript.sh > out.txt
$ cat out.txt
    0 pipe 
    1 file /tmp/out.txt
    2 tty /dev/pts/1

Prawdopodobnie trzeba będzie zainstalować filanz sudo apt install socatpierwszym.

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.