stdin
, stdout
, dan stderr
adalah aliran dilampirkan ke masing-masing deskriptor file 0, 1, dan 2 dari suatu proses.
Pada prompt shell interaktif di terminal atau emulator terminal, ketiga deskriptor file tersebut akan merujuk ke deskripsi file terbuka yang sama yang akan diperoleh dengan membuka file perangkat terminal atau pseudo-terminal (sesuatu seperti /dev/pts/0
) dalam mode baca+tulis.
Jika dari shell interaktif tersebut, Anda memulai skrip tanpa menggunakan pengalihan apa pun, skrip Anda akan mewarisi deskriptor file tersebut.
Di Linux, /dev/stdin
, /dev/stdout
, /dev/stderr
adalah tautan simbolik ke /proc/self/fd/0
, /proc/self/fd/1
, /proc/self/fd/2
masing-masing, symlink khusus itu sendiri ke file aktual yang dibuka pada deskriptor file tersebut.
Mereka bukan stdin, stdout, stderr, mereka adalah file khusus yang mengidentifikasi file apa yang akan digunakan stdin, stdout, stderr (perhatikan bahwa ini berbeda di sistem lain selain Linux yang memiliki file khusus tersebut).
membaca sesuatu dari stdin berarti membaca dari deskriptor file 0 (yang akan menunjuk ke suatu tempat di dalam file yang direferensikan oleh /dev/stdin
).
Tapi di $(</dev/stdin)
, shell tidak membaca dari stdin, ia membuka deskriptor file baru untuk membaca di file yang sama dengan yang dibuka di stdin (jadi membaca dari awal file, bukan ke tempat stdin saat ini).
Kecuali dalam kasus khusus perangkat terminal terbuka dalam mode baca+tulis, stdout dan stderr biasanya tidak terbuka untuk dibaca. Itu dimaksudkan untuk menjadi aliran yang Anda tulisi . Jadi membaca dari deskriptor file 1 umumnya tidak akan berfungsi. Di Linux, membuka /dev/stdout
atau /dev/stderr
untuk membaca (seperti pada $(</dev/stdout)
) akan berfungsi dan akan membiarkan Anda membaca dari file tempat stdout pergi (dan jika stdout adalah sebuah pipa, itu akan membaca dari ujung pipa yang lain, dan jika itu adalah soket, itu akan gagal karena Anda tidak bisa buka soket).
Dalam kasus skrip kami dijalankan tanpa pengalihan pada prompt shell interaktif di terminal, semua /dev/stdin, /dev/stdout dan /dev/stderr akan menjadi file perangkat terminal /dev/pts/x itu.
Membaca dari file-file khusus itu mengembalikan apa yang dikirim oleh terminal (apa yang Anda ketik di keyboard). Menulis kepada mereka akan mengirim teks ke terminal (untuk ditampilkan).
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 perluasan (apa yang Anda ketikkan dihapus dari baris baru yang tertinggal dan tunduk pada split+glob) ke echo
yang kemudian akan menampilkannya di stdout (untuk ditampilkan).
Namun di:
echo $(</dev/stdout)
di bash
(dan bash
saja), penting untuk disadari bahwa di dalam $(...)
, stdout telah dialihkan. Sekarang menjadi pipa. Dalam kasus bash
, proses shell anak sedang membaca konten file (di sini /dev/stdout
) dan menuliskannya ke dalam pipa, sedangkan induk membaca dari ujung yang lain untuk membentuk perluasan.
Dalam hal ini ketika proses bash anak itu membuka /dev/stdout
, itu sebenarnya membuka ujung pembacaan pipa. Tidak ada yang akan datang dari itu, ini adalah situasi kebuntuan.
Jika Anda ingin membaca dari file yang ditunjuk oleh skrip stdout, Anda dapat 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 di out
setelah itu:
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 lebih tidak masuk akal), Anda akan melakukannya:
#! /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 skrip kedua itu lagi 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 terbuka untuk menulis hanya, 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
(di mana <>
terbuka 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 printf
sebelumnya telah menimpa konten out
dengan what I read from stdin: foo\n
dan meninggalkan posisi stdout di dalam file itu setelahnya. Jika Anda telah mempersiapkan out
dengan beberapa teks yang lebih besar, seperti:
echo 'This is longer than "what I read from stdin": foo' > out
Kemudian Anda akan masuk ke 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 printf
pertama dan melakukan itu juga memindahkan posisi stdout melewatinya sehingga printf
berikutnya menampilkan apa yang dibaca setelahnya.
stdout
dan stderr
adalah keluaran, Anda tidak membaca darinya, Anda hanya dapat menulisnya. Misalnya:
echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr
program menulis ke stdout secara default sehingga yang pertama setara dengan
echo "this is stdout"
dan Anda dapat mengarahkan ulang stderr dengan cara lain seperti
echo "this is stderr" 1>&2