echo hoặc in / dev / stdin / dev / stdout / dev / stderr


13

Tôi muốn in giá trị của / dev / stdin, / dev / stdout và / dev / stderr.

Đây là kịch bản đơn giản của tôi:

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

tôi sử dụng các đường ống sau:

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

$(</dev/stdin)dường như chỉ hoạt động, tôi cũng tìm thấy trên một số câu hỏi khác mà mọi người sử dụng: "${1-/dev/stdin}"đã thử nó mà không thành công.

Câu trả lời:


29

stdin, stdoutstderrcác luồng được đính kèm với các mô tả tệp 0, 1 và 2 tương ứng của một quy trình.

Theo dấu nhắc của trình bao tương tác trong trình giả lập thiết bị đầu cuối hoặc thiết bị đầu cuối, tất cả 3 mô tả tệp đó sẽ tham chiếu đến cùng một mô tả tệp mở có được bằng cách mở tệp thiết bị đầu cuối hoặc giả thiết bị đầu cuối (giống như /dev/pts/0) trong read + write chế độ.

Nếu từ lớp vỏ tương tác đó, bạn bắt đầu tập lệnh của mình mà không sử dụng bất kỳ chuyển hướng nào, tập lệnh của bạn sẽ kế thừa các mô tả tệp đó.

Trên Linux, /dev/stdin, /dev/stdout, /dev/stderrlà các liên kết tượng trưng cho /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2tương ứng, tự liên kết tượng trưng đặc biệt đến tập tin thực tế đó là mở cửa vào những file descriptor.

Chúng không phải là stdin, stdout, stderr, chúng là các tệp đặc biệt xác định các tệp stdin, stdout, stderr đi tới (lưu ý rằng nó khác với các hệ thống khác ngoài Linux có các tệp đặc biệt đó).

đọc một cái gì đó từ stdin có nghĩa là đọc từ mô tả tệp 0 (sẽ chỉ ra một nơi nào đó trong tệp được tham chiếu bởi /dev/stdin).

Nhưng trong $(</dev/stdin), shell không đọc từ stdin, nó mở một bộ mô tả tệp mới để đọc trên cùng một tệp với tệp mở trên stdin (vì vậy đọc từ đầu tệp chứ không phải nơi stdin hiện đang trỏ đến).

Ngoại trừ trong trường hợp đặc biệt của các thiết bị đầu cuối mở ở chế độ đọc + ghi, thiết bị xuất chuẩn và thiết bị xuất chuẩn thường không mở để đọc. Chúng có nghĩa là các luồng mà bạn viết . Vì vậy, đọc từ mô tả tập tin 1 thường sẽ không hoạt động. Trên Linux, mở /dev/stdouthoặc /dev/stderrđể đọc (như trong $(</dev/stdout)) sẽ hoạt động và sẽ cho phép bạn đọc từ tệp mà thiết bị xuất chuẩn đi tới (và nếu thiết bị xuất chuẩn là một ống, nó sẽ đọc từ đầu kia của ống và nếu đó là ổ cắm , nó sẽ thất bại khi bạn không thể mở một ổ cắm).

Trong trường hợp tập lệnh của chúng tôi chạy mà không chuyển hướng theo dấu nhắc của trình bao tương tác trong thiết bị đầu cuối, tất cả / dev / stdin, / dev / stdout và / dev / stderr sẽ là tập tin thiết bị đầu cuối / dev / pts / x.

Đọc từ các tệp đặc biệt đó trả về những gì được gửi bởi thiết bị đầu cuối (những gì bạn gõ trên bàn phím). Viết cho họ sẽ gửi văn bản đến thiết bị đầu cuối (để hiển thị).

echo $(</dev/stdin)
echo $(</dev/stderr)

sẽ giống nhau. Để mở rộng $(</dev/stdin), shell sẽ mở / dev / pts / 0 và đọc những gì bạn gõ cho đến khi bạn nhấn ^Dvào một dòng trống. Sau đó, họ sẽ vượt qua bản mở rộng (những gì bạn đã nhập tước dòng mới và theo phân tách + global) echosau đó sẽ xuất nó trên thiết bị xuất chuẩn (để hiển thị).

Tuy nhiên trong:

echo $(</dev/stdout)

trong bash( bashduy nhất ), điều quan trọng là phải nhận ra rằng bên trong $(...), thiết bị xuất chuẩn đã được chuyển hướng. Bây giờ nó là một đường ống. Trong trường hợp bash, một quá trình shell con đang đọc nội dung của tệp (ở đây /dev/stdout) và ghi nó vào ống, trong khi cha mẹ đọc từ đầu kia để tạo ra sự mở rộng.

Trong trường hợp này khi quá trình bash con đó mở ra /dev/stdout, nó thực sự đang mở đầu đọc của ống. Sẽ không có gì đến từ đó, đó là một tình huống bế tắc.

Nếu bạn muốn đọc từ tệp được chỉ ra bởi thiết bị xuất chuẩn, bạn sẽ làm việc với nó:

 { echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1

Điều đó sẽ nhân đôi fd 1 lên fd 3, vì vậy / dev / fd / 3 sẽ trỏ đến cùng một tệp là / dev / stdout.

Với một kịch bản như:

#! /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)"

Khi chạy như:

echo bar > err
echo foo | myscript > out 2>> err

Bạn sẽ thấy outsau đó:

content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar

Nếu như trái ngược với đọc từ /dev/stdin, /dev/stdout, /dev/stderr, bạn muốn đọc từ stdin, stdout và stderr (mà sẽ làm cho thậm chí còn ít ý nghĩa), bạn sẽ làm:

#! /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)"

Nếu bạn bắt đầu tập lệnh thứ hai đó một lần nữa như:

echo bar > err
echo foo | myscript > out 2>> err

Bạn sẽ thấy trong out:

what I read from stdin: foo
what I read from stdout:
what I read from stderr:

và trong err:

bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor

Đối với thiết bị xuất chuẩn và thiết bị xuất chuẩn, catthất bại vì các bộ mô tả tệp chỉ mở để viết , không đọc, việc mở rộng $(cat <&3)$(cat <&2)trống.

Nếu bạn gọi nó là:

echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err

(nơi <>mở ở chế độ đọc + ghi mà không cắt ngắn), bạn sẽ thấy trong out:

what I read from stdin: foo
what I read from stdout:
what I read from stderr: err

và trong err:

err

Bạn sẽ nhận thấy rằng không có gì được đọc từ thiết bị xuất chuẩn, bởi vì trước đó printfđã bị ghi đè nội dung của outvới what I read from stdin: foo\nvà rời khỏi vị trí stdout trong tập tin đó ngay sau. Nếu bạn đã mồi outvới một số văn bản lớn hơn, như:

echo 'This is longer than "what I read from stdin": foo' > out

Sau đó, bạn sẽ nhận được vào 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

Xem làm thế nào $(cat <&3)đã đọc những gì còn lại sau lần đầu tiên printf và làm như vậy cũng di chuyển vị trí xuất sắc qua nó để các printfđầu ra tiếp theo những gì được đọc sau đó.


2

stdoutstderrlà kết quả đầu ra, bạn không đọc từ chúng, bạn chỉ có thể viết cho họ. Ví dụ:

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

các chương trình ghi vào thiết bị xuất chuẩn theo mặc định để chương trình đầu tiên tương đương với

echo "this is stdout"

và bạn có thể chuyển hướng stderr theo những cách khác như

echo "this is stderr" 1>&2

1
Trên Linux, echo xkhông giống như echo x > /dev/stdoutkhi thiết bị xuất chuẩn không đi đến đường ống hoặc một số thiết bị ký tự như thiết bị tty. Chẳng hạn, nếu thiết bị xuất chuẩn đi đến một tệp thông thường echo x > /dev/stdoutsẽ cắt bớt tệp và thay thế nội dung của nó bằng x\nthay vì ghi x\nở vị trí xuất chuẩn hiện tại.
Stéphane Chazelas

@ Michael-Daffin> Cảm ơn bạn, vì vậy nếu tôi muốn đọc stdout và stderr tôi chỉ có thể sử dụng mèo? (vì chúng là tệp), ví dụ tôi muốn cho người dùng thấy lỗi cuối cùng xảy ra echo this is your error $(cat /dev/stderr)?
Omar BISTAMI

@OmarBISTAMI Bạn không thể đọc từ stdoutstderr, chúng là kết quả đầu ra.
Kusalananda

1

myscript.sh:

#!/bin/bash
filan -s

Sau đó chạy:

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

Bạn có thể sẽ cần phải cài đặt filanvới sudo apt install socatđầu tiên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.