Linux

回顯或列印 /dev/stdin /dev/stdout /dev/stderr

  • November 2, 2018

我想列印/dev/stdin、/dev/stdout 和/dev/stderr 的值。

這是我的簡單腳本:

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

我使用以下管道:

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

似乎只有$(</dev/stdin)工作,我還發現了人們使用的其他一些問題:"${1-/dev/stdin}"嘗試過但沒有成功。

stdinstdoutstderr是分別附加到程序的文件描述符0、1 和 2 的流。

在終端或終端模擬器中的互動式 shell 提示下,所有這 3 個文件描述符將引用相同的打開文件描述/dev/pts/0,這將通過以讀+寫方式打開終端或偽終端設備文件(類似)獲得模式。

如果從該互動式 shell 啟動腳本而不使用任何重定向,則腳本將繼承這些文件描述符。

在 Linux 上,/dev/stdin, /dev/stdout,分別是指向,/dev/stderr的符號連結,它們本身就是指向在這些文件描述符上打開的實際文件的特殊符號連結。/proc/self/fd/0``/proc/self/fd/1``/proc/self/fd/2

它們不是 stdin、stdout、stderr,它們是特殊文件,用於標識 stdin、stdout、stderr 轉到哪些文件(請注意,在具有這些特殊文件的 Linux 以外的其他系統中,它是不同的)。

從標準輸入讀取某些內容意味著從文件描述符 0 讀取(它將指向 引用的文件中的某個位置/dev/stdin)。

但是在 中$(</dev/stdin),shell 沒有從 stdin 讀取,它打開一個新的文件描述符,用於讀取與在 stdin 上打開的文件相同的文件(因此從文件的開頭讀取,而不是 stdin 目前指向的位置)。

除了終端設備以讀寫方式打開的特殊情況外,stdout和stderr通常是不開放讀取的。它們是您寫入的流。所以從文件描述符 1 讀取通常不會起作用。在 Linux 上,打開/dev/stdout/dev/stderr讀取(如 in $(</dev/stdout))將起作用,並讓您從 stdout 所在的文件中讀取(如果 stdout 是管道,則將從管道的另一端讀取,如果它是套接字,它會失敗,因為你不能打開一個套接字)。

在我們的腳本在終端互動式 shell 提示下執行而不重定向的情況下,所有 /dev/stdin、/dev/stdout 和 /dev/stderr 都將是 /dev/pts/x 終端設備文件。

從這些特殊文件中讀取會返回終端發送的內容(您在鍵盤上鍵入的內容)。寫信給他們會將文本發送到終端(用於顯示)。

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

將是相同的。要展開$(</dev/stdin),shell 將打開 /dev/pts/0 並讀取您鍵入的內容,直到您按下^D一個空行。然後他們將傳遞擴展(您鍵入的內容去除了尾隨換行符並受到 split+glob 的影響),echo然後將其輸出到標準輸出(用於顯示)。

然而在:

echo $(</dev/stdout)

bash(bash) 中,重要的是要意識到在內部$(...),標準輸出已被重定向。它現在是一個管道。在 的情況下bash,子 shell 程序正在讀取文件的內容(此處/dev/stdout)並將其寫入管道,而父程序從另一端讀取以彌補擴展。

在這種情況下,當那個子 bash 程序打開時/dev/stdout,它實際上是在打開管道的讀取端。不會有任何結果,這是一個僵局。

如果您想從腳本標準輸出指向的文件中讀取,您可以使用以下方法解決它:

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

這會將 fd 1 複製到 fd 3 上,因此 /dev/fd/3 將指向與 /dev/stdout 相同的文件。

使用如下腳本:

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

當執行為:

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

之後你會看到out

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

如果不是從/dev/stdin, /dev/stdout,讀取/dev/stderr,您想從 stdin、stdout 和 stderr 讀取(這將更沒有意義),您會這樣做:

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

如果您再次啟動第二個腳本:

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

你會看到out

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

並在err

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

對於 stdout 和 stderr,cat失敗是因為文件描述符只為寫入而不是讀取而打開,並且擴展$(cat <&3)$(cat <&2)空。

如果您將其稱為:

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

(在<>沒有截斷的情況下以讀+寫模式打開),你會看到out

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

並在err

err

您會注意到沒有從 stdout 讀取任何內容,因為之前printf的內容已經覆蓋了outwith的內容,what I read from stdin: foo\n並在之後將 stdout 位置留在了該文件中。如果您out使用一些較大的文本進行了準備,例如:

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

然後你會進入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

查看如何$(cat <&3)讀取第一個之後剩下的內容printf **,這樣做也將 stdout 位置移過它,**以便下一個printf輸出之後讀取的內容。

引用自:https://unix.stackexchange.com/questions/400849