gema atau cetak / dev / stdin / dev / stdout / dev / stderr

13

Saya ingin mencetak nilai / dev / stdin, / dev / stdout dan / dev / stderr.

Berikut ini skrip sederhana saya:

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

saya menggunakan pipa berikut:

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

$(</dev/stdin)sepertinya hanya berfungsi, saya juga menemukan beberapa pertanyaan lain yang digunakan orang: "${1-/dev/stdin}"mencobanya tanpa hasil.

Omar BISTAMI
sumber

Jawaban:

29

stdin, stdout, Dan stderradalah 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/stderradalah link simbolik ke /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2masing-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/stdoutatau /dev/stderrmembaca (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 ^Dpada baris kosong. Mereka kemudian akan meneruskan ekspansi (apa yang Anda ketikkan dari baris baru yang tertinggal dan dapat dibagi + glob) echoyang kemudian akan menampilkannya di stdout (untuk tampilan).

Namun dalam:

echo $(</dev/stdout)

di bash( dan bashhanya ), 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 outsetelahnya:

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, catgagal 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 printftelah menimpa konten outdengan what I read from stdin: foo\ndan meninggalkan posisi stdout dalam file itu setelahnya. Jika Anda telah menyiapkan outbeberapa 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 printfmenampilkan apa yang dibaca setelah itu.

Stéphane Chazelas
sumber
2

stdoutdan stderrmerupakan keluaran, Anda tidak membacanya, Anda hanya bisa menulis kepada mereka. Sebagai contoh:

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 stderr dengan cara lain seperti

echo "this is stderr" 1>&2
Michael Daffin
sumber
1
Di Linux, echo xtidak sama seperti echo x > /dev/stdoutjika stdout tidak pergi ke pipa atau beberapa perangkat karakter seperti perangkat tty. Misalnya, jika stdout pergi ke file biasa echo x > /dev/stdoutakan memotong file dan mengganti kontennya dengan x\nalih - alih menulis x\npada posisi stdout saat ini.
Stéphane Chazelas
@ Michael-Daffin> Terima kasih, jadi jika saya ingin membaca stdout dan stderr saya hanya bisa menggunakan cat? (karena mereka file), misalnya saya ingin menunjukkan kepada pengguna kesalahan terakhir yang terjadi echo this is your error $(cat /dev/stderr)?
Omar BISTAMI
@ OmarBISTAMI Anda tidak dapat membaca dari stdoutdan stderr, mereka adalah output.
Kusalananda
1

myscript.sh:

#!/bin/bash
filan -s

Lalu lari:

$ ./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

Anda mungkin akan perlu menginstal filandengan sudo apt install socatpertama.

wisbucky
sumber