回显或打印/ dev / stdin / dev / stdout / dev / stderr


13

我想打印/ 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}"尝试失败。

Answers:


29

stdinstdoutstderr是分别附加到进程的文件描述符 0、1和2的

在终端或终端仿真器中的交互式外壳程序提示下,所有这三个文件描述符都将引用相同的打开文件描述,该描述可以通过/dev/pts/0以读写方式打开终端或伪终端设备文件(如)获得。模式。

如果从该交互式外壳程序启动脚本而不使用任何重定向,则脚本将继承这些文件描述符。

在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),外壳程序将打开/ dev / pts / 0并读取您键入的内容,直到您按^D空白行。然后,它们将传递扩展名(您键入的内容以尾随换行符的形式去除,并受split + glob限制),然后将其扩展到echo标准输出(用于显示)。

但是在:

echo $(</dev/stdout)

bashbash)中,重要的是要意识到inside $(...),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,你想从标准输入,输出和错误(这将使甚至更少的意义上)阅读,你会怎么做:

#! /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)并且and 的扩展$(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

看看s如何$(cat <&3)读取第一个之后剩下的内容,并将printf stdout位置移到该位置之后,以便下一个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

1
在Linux上,echo x这与echo x > /dev/stdoutstdout不会进入管道或某些字符设备(如tty设备)不同。例如,如果将stdout转到常规文件,echo x > /dev/stdout则会截断该文件并替换其内容,x\n而不是x\n在当前stdout位置写入。
斯特凡Chazelas

@ Michael-Daffin>谢谢,所以如果我想阅读stdout和stderr,我只能使用cat吗?(因为它们是文件),例如,我想向用户显示最近发生的错误echo this is your error $(cat /dev/stderr)
奥马尔·比斯塔米

@OmarBISTAMI您无法读取stdoutstderr,它们是输出。
库萨兰达

1

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

你可能会需要安装filansudo apt install socat第一。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.