我想打印 /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}"
尝试过但没有成功。
答案1
stdin
、stdout
、 和stderr
是溪流附于文件描述符分别为一个进程的 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 不同)。
从 stdin 读取某些内容意味着从文件描述符 0 读取(它将指向 引用的文件中的某个位置/dev/stdin
)。
但是在 中$(</dev/stdin)
,shell 不是从 stdin 读取,而是打开一个新的文件描述符,用于读取与 stdin 上打开的文件相同的文件(因此从文件的开头读取,而不是 stdin 当前指向的位置)。
除了以读+写模式打开终端设备的特殊情况外,stdout 和 stderr 通常不打开进行读取。它们应该是您可以使用的流写给。所以从文件描述符1读取一般是行不通的。在 Linux 上,打开/dev/stdout
或/dev/stderr
读取(如 中所示$(</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
只有),重要的是要意识到在内部$(...)
,stdout 已经被重定向。现在它是一个管道。在 的情况下bash
,子 shell 进程正在读取文件的内容(此处为/dev/stdout
)并将其写入管道,而父进程则从另一端读取以进行扩展。
在这种情况下,当子 bash 进程打开时/dev/stdout
,它实际上是在打开管道的读取端。这样做不会有任何结果,这是一个僵局。
如果您想从脚本 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 读取任何内容,因为前一个已经覆盖了withprintf
的内容,并在之后留下了该文件中的 stdout 位置。如果您已经涂上了一些较大的文字,例如:out
what I read from stdin: foo\n
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
这样做也会将标准输出位置移过去以便下一个printf
输出之后读取的内容。
答案2
stdout
和stderr
是输出,您不能读取它们,只能写入它们。例如:
echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr
程序默认写入标准输出,因此第一个相当于
echo "this is stdout"
您可以通过其他方式重定向 stderr,例如
echo "this is stderr" 1>&2
答案3
myscript.sh:
#!/bin/bash
filan -s
然后运行:
$ ./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
您可能需要filan
先安装sudo apt install socat
。