stdin
, stdout
, Dan stderr
adalah sungai yang melekat pada berkas deskriptor 0, 1, dan 2 masing-masing dari suatu proses.
Pada prompt shell interaktif di terminal atau terminal emulator, ketiga deskriptor file tersebut akan merujuk ke deskripsi file terbuka yang sama yang akan diperoleh dengan membuka terminal atau file perangkat pseudo-terminal (seperti /dev/pts/0
) pada read + write mode.
Jika dari shell interaktif itu, Anda memulai skrip Anda tanpa menggunakan pengalihan apa pun, skrip Anda akan mewarisi deskriptor file tersebut.
Pada Linux, /dev/stdin
, /dev/stdout
, /dev/stderr
adalah link simbolik ke /proc/self/fd/0
, /proc/self/fd/1
, /proc/self/fd/2
masing-masing, diri symlink khusus untuk file yang sebenarnya yang terbuka pada orang-orang file deskriptor.
Mereka bukan stdin, stdout, stderr, mereka adalah file khusus yang mengidentifikasi file apa stdin, stdout, stderr pergi ke (perhatikan bahwa itu berbeda di sistem lain daripada Linux yang memiliki file-file khusus).
membaca sesuatu dari stdin berarti membaca dari file descriptor 0 (yang akan menunjuk ke suatu tempat di dalam file yang dirujuk oleh /dev/stdin
).
Tetapi pada $(</dev/stdin)
, shell tidak membaca dari stdin, ia membuka file descriptor baru untuk membaca pada file yang sama dengan yang terbuka pada stdin (jadi membaca dari awal file, bukan di mana stdin saat ini menunjuk ke).
Kecuali dalam kasus khusus perangkat terminal terbuka dalam mode baca + tulis, stdout dan stderr biasanya tidak terbuka untuk dibaca. Itu dimaksudkan sebagai aliran yang Anda tulis . Jadi membaca dari file deskriptor 1 umumnya tidak akan berfungsi. Di Linux, membuka /dev/stdout
atau /dev/stderr
membaca (seperti pada $(</dev/stdout)
) akan bekerja dan akan membiarkan Anda membaca dari file di mana stdout pergi ke (dan jika stdout adalah pipa, itu akan membaca dari ujung pipa yang lain, dan jika itu adalah soket , itu akan gagal karena Anda tidak dapat membuka soket).
Dalam kasus skrip kami berjalan tanpa pengalihan pada prompt shell interaktif di terminal, semua / dev / stdin, / dev / stdout dan / dev / stderr akan menjadi file perangkat terminal / dev / pts / x terminal.
Membaca dari file-file khusus mengembalikan apa yang dikirim oleh terminal (apa yang Anda ketik pada keyboard). Menulis kepada mereka akan mengirim teks ke terminal (untuk tampilan).
echo $(</dev/stdin)
echo $(</dev/stderr)
akan tetap sama. Untuk memperluas $(</dev/stdin)
, shell akan membuka / dev / pts / 0 itu dan membaca apa yang Anda ketik sampai Anda menekan ^D
pada baris kosong. Mereka kemudian akan meneruskan ekspansi (apa yang Anda ketikkan dari baris baru yang tertinggal dan dapat dibagi + glob) echo
yang kemudian akan menampilkannya di stdout (untuk tampilan).
Namun dalam:
echo $(</dev/stdout)
di bash
( dan bash
hanya ), penting untuk menyadari bahwa di dalam $(...)
, stdout telah diarahkan. Sekarang menjadi pipa. Dalam kasus bash
, proses shell anak membaca konten file (di sini /dev/stdout
) dan menulisnya ke pipa, sementara orang tua membaca dari ujung yang lain untuk membuat ekspansi.
Dalam hal ini ketika proses pesta anak itu dibuka /dev/stdout
, sebenarnya membuka ujung pembacaan pipa. Tidak ada yang akan datang dari situ, ini adalah situasi jalan buntu.
Jika Anda ingin membaca dari file yang diarahkan oleh skrip stdout, Anda akan mengatasinya dengan:
{ echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1
Itu akan menduplikasi fd 1 ke fd 3, jadi / dev / fd / 3 akan menunjuk ke file yang sama dengan / dev / stdout.
Dengan skrip seperti:
#! /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)"
Saat dijalankan sebagai:
echo bar > err
echo foo | myscript > out 2>> err
Anda akan melihat out
setelahnya:
content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
Jika dibandingkan dengan membaca dari /dev/stdin
, /dev/stdout
, /dev/stderr
, Anda ingin membaca dari stdin, stdout dan stderr (yang akan membuat bahkan kurang akal), Anda akan melakukan:
#! /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)"
Jika Anda memulai kembali skrip kedua sebagai:
echo bar > err
echo foo | myscript > out 2>> err
Anda akan melihat di out
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr:
dan di err
:
bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor
Untuk stdout dan stderr, cat
gagal karena deskriptor file hanya terbuka untuk menulis , tidak membaca, perluasan $(cat <&3)
dan $(cat <&2)
kosong.
Jika Anda menyebutnya sebagai:
echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err
(ketika <>
dibuka dalam mode baca + tulis tanpa pemotongan), Anda akan melihat di out
:
what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
dan di err
:
err
Anda akan melihat bahwa tidak ada yang dibaca dari stdout, karena sebelumnya printf
telah menimpa konten out
dengan what I read from stdin: foo\n
dan meninggalkan posisi stdout dalam file itu setelahnya. Jika Anda telah menyiapkan out
beberapa teks yang lebih besar, seperti:
echo 'This is longer than "what I read from stdin": foo' > out
Maka Anda akan masuk 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
Lihat bagaimana $(cat <&3)
telah membaca apa yang tersisa setelah yang pertama printf
dan melakukannya juga memindahkan posisi stdout melewatinya sehingga selanjutnya printf
menampilkan apa yang dibaca setelah itu.
echo x
tidak sama sepertiecho x > /dev/stdout
jika stdout tidak pergi ke pipa atau beberapa perangkat karakter seperti perangkat tty. Misalnya, jika stdout pergi ke file biasaecho x > /dev/stdout
akan memotong file dan mengganti kontennya denganx\n
alih - alih menulisx\n
pada posisi stdout saat ini.