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

Oma*_*AMI 21 linux shell shell-script stdin

我想打印 /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)
Run Code Online (Sandbox Code Playgroud)

我使用以下管道:

[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh
Run Code Online (Sandbox Code Playgroud)

只是$(</dev/stdin)似乎工作,我也对其他一些问题的人在使用中发现:"${1-/dev/stdin}"试了一下没有成功。

Sté*_*las 40

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

在终端或终端模拟器中的交互式 shell 的提示下,所有这 3 个文件描述符都将引用相同的打开文件描述,该描述将通过/dev/pts/0以 read+write方式打开终端或伪终端设备文件(类似)而获得模式。

如果从该交互式 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)
Run Code Online (Sandbox Code Playgroud)

将是相同的。要展开$(</dev/stdin),shell 将打开该 /dev/pts/0 并读取您键入的内容,直到您按下^D空行。然后他们会将扩展(您键入的内容去掉尾随的换行符并受 split+glob 约束)传递给echo它,然后将其输出到 stdout(用于显示)。

然而在:

echo $(</dev/stdout)
Run Code Online (Sandbox Code Playgroud)

bashbash)中,重要的是要意识到在内部$(...),标准输出已被重定向。它现在是一个管道。在 的情况下bash,子 shell 进程正在读取文件的内容(此处/dev/stdout)并将其写入管道,而父进程从另一端读取以弥补扩展。

在这种情况下,当那个子 bash 进程打开时/dev/stdout,它实际上是在打开管道的读取端。什么都不会发生,这是一个僵局。

如果您想从脚本 stdout 指向的文件中读取数据,您可以使用以下方法解决它:

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

这会将 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)"
Run Code Online (Sandbox Code Playgroud)

当运行为:

echo bar > err
echo foo | myscript > out 2>> err
Run Code Online (Sandbox Code Playgroud)

之后你会看到out

content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar
Run Code Online (Sandbox Code Playgroud)

如果与从/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)"
Run Code Online (Sandbox Code Playgroud)

如果您再次启动第二个脚本:

echo bar > err
echo foo | myscript > out 2>> err
Run Code Online (Sandbox Code Playgroud)

你会看到out

what I read from stdin: foo
what I read from stdout:
what I read from stderr:
Run Code Online (Sandbox Code Playgroud)

并在err

bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor
Run Code Online (Sandbox Code Playgroud)

对于 stdout 和 stderr,cat失败是因为文件描述符是为只而不是读而打开的,$(cat <&3)并且 和的扩展$(cat <&2)为空。

如果您将其称为:

echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err
Run Code Online (Sandbox Code Playgroud)

(在<>没有截断的情况下以读+写模式打开),你会看到out

what I read from stdin: foo
what I read from stdout:
what I read from stderr: err
Run Code Online (Sandbox Code Playgroud)

并在err

err
Run Code Online (Sandbox Code Playgroud)

您会注意到没有从 stdout 中读取任何printf内容,因为之前的内容已经覆盖了outwith的内容,what I read from stdin: foo\n并紧随其后将 stdout 位置留在了该文件中。如果您out使用了一些较大的文本,例如:

echo 'This is longer than "what I read from stdin": foo' > out
Run Code Online (Sandbox Code Playgroud)

然后你会进入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
Run Code Online (Sandbox Code Playgroud)

查看如何$(cat <&3)读取第一个之后剩下的内容,printf 并且这样做还将 stdout 位置移过它,以便下一个printf输出之后读取的内容。