什么是Linux上的stdin,stdout和stderr?

时间:2020-01-09 10:44:09  来源:igfitidea点击:

" stdin"," stdout"和" stderr"是我们启动Linux命令时创建的三个数据流。我们可以使用它们来告诉脚本是通过管道传输还是重定向。我们向我们展示如何。

流连接两点

一旦开始学习Linux和类似Unix的操作系统,就会遇到术语" stdin"," stdout"和" stederr"。这是执行Linux命令时建立的三个标准流。在计算中,流是可以传输数据的东西。对于这些流,该数据是文本。

数据流就像水流一样,有两个末端。他们有一个来源和一个流出。我们所使用的任何Linux命令都提供每个流的一端。另一端由启动命令的shell程序确定。根据启动命令的命令行,该端将连接到终端窗口,连接到管道或者重定向到文件或者其他命令。

Linux标准流

在Linux中," stdin"是标准输入流。这接受文本作为输入。从命令输出到shell的文本是通过stdout(标准输出)流传递的。来自命令的错误消息通过" stderr"(标准错误)流发送。

因此,我们可以看到有两个输出流" stdout"和" stderr",以及一个输入流" stdin"。由于错误消息和正常输出各自具有将其携带到终端窗口的管道,因此可以彼此独立地进行处理。

流像文件一样处理

Linux中的流几乎像其他所有内容一样被视为文件。我们可以从文件中读取文本,也可以将文本写入文件中。这两个动作都涉及数据流。因此,将数据流作为文件处理的概念并不是一件容易的事。

与进程关联的每个文件都分配有一个唯一的编号以进行标识。这称为文件描述符。每当需要对文件执行操作时,都使用文件描述符来标识文件。

这些值始终用于" stdin"," stdout"和" stderr":

  • 0:标准输入

  • 1:标准输出

  • 2:stderr

对管道和重定向做出反应

为了简化某人对该主题的介绍,一种常见的技术是教授该主题的简化版本。例如,在语法上,我们被告知该规则是E之前的I,除了C之后的I。但是,实际上,该规则比在某些情况下服从的例外更多。

同样,在谈论" stdin"," stdout"和" stderr"时,方便地抛出公认的公理,该公理既不知道也不关心其三个标准流的终止位置。进程是否应该在乎其输出是发送到终端还是重定向到文件?它甚至可以判断其输入是来自键盘还是正从另一个进程通过管道输入?

实际上,如果软件作者决定添加该功能,则该过程至少知道了它是否可以发现,如果它选择检查并可以相应地更改其行为。

我们可以很容易地看到这种行为上的变化。尝试以下两个命令:

ls
ls | cat

如果" ls"命令的输出(" stdout")被传递到另一个命令中,则其行为会有所不同。切换到单列输出的是ls,而不是cat所执行的转换。如果ls的输出被重定向,它也会做同样的事情:

ls > capture.txt
cat capture.txt

重定向标准输出和标准错误

通过专用流传递错误消息有一个优势。这意味着我们可以将命令的输出(stdout)重定向到文件,并且仍然可以在终端窗口中看到任何错误消息(stderr)。我们可以根据需要对错误进行处理,以防发生错误。它还可以防止错误消息污染已将" stdout"重定向到的文件。

在编辑器中键入以下文本,然后将其保存到名为error.sh的文件中。

#!/bin/bash

echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

使用以下命令使脚本可执行:

chmod +x error.sh

脚本的第一行通过stdout流将文本回显到终端窗口。第二行尝试访问不存在的文件。这将生成一条通过stderr发送的错误消息。

使用以下命令运行脚本:

./error.sh

我们可以看到在终端窗口中已经显示了输出流stdout和stderr。

让我们尝试将输出重定向到文件:

./error.sh > capture.txt

通过stderr传递的错误消息仍然发送到终端窗口。我们可以检查文件的内容以查看stdout输出是否进入了文件。

cat capture.txt

stdin的输出已按预期重定向到文件。

默认情况下,">"重定向符号与" stdout"一起使用。我们可以使用数字文件描述符之一来指示要重定向的标准输出流。

要显式重定向" stdout",请使用以下重定向指令:

1>

要显式重定向stderr,请使用以下重定向指令:

2>

让我们再次尝试测试,这次我们使用2>

./error.sh 2> capture.txt

错误消息被重定向,并且stdout``echo消息被发送到终端窗口:

让我们看看capture.txt文件中的内容。

cat capture.txt

如预期的那样,stderr消息在capture.txt中。

重定向stdout和stderr

当然,如果我们可以将stdout或者stderr相互重定向到一个文件,我们应该能够同时将它们重定向到两个不同的文件吗?

我们可以。此命令会将" stdout"定向到名为capture.txt的文件,并将" stderr"定向到名为error.txt的文件。

./error.sh 1> capture.txt 2> error.txt

因为outputstandard输出和standard错误流都重定向到文件,所以终端窗口中没有可见的输出。就像什么都没有发生一样,我们返回到命令行提示符。

让我们检查每个文件的内容:

cat capture.txt
cat error.txt

将stdout和stderr重定向到同一文件

太好了,我们已经将每个标准输出流放入其自己的专用文件中。我们唯一可以做的组合就是将stdoutstderr发送到同一文件。

我们可以使用以下命令来实现:

./error.sh > capture.txt 2>&1

让我们分解一下。

  • ./error.sh:启动error.sh脚本文件。

  • capture.txt:将stdout流重定向到capture.txt文件。 ">"是" 1>"的简写。

  • 2>&1:这使用&>重定向指令。此指令使我们可以告诉Shell使一个流与另一个流到达相同的目的地。在这种情况下,我们说的是将流2" stderr"重定向到流1" stdout"被重定向到的相同目的地。

没有可见的输出。令人鼓舞。

让我们检查capture.txt文件并查看其中的内容。

cat capture.txt

stdoutstderr流都已重定向到单个目标文件。

要重定向并静默丢弃流的输出,请将输出定向到/ dev / null

在脚本中检测重定向

我们讨论了命令如何检测是否有任何流被重定向,并可以选择相应地更改其行为。我们可以在自己的脚本中完成此操作吗?我们可以。这是一种非常容易理解和使用的技术。

在编辑器中键入以下文本,并将其另存为input.sh。

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

使用以下命令使其可执行:

chmod +x input.sh

聪明的部分是方括号内的测试。如果与文件描述符关联的文件在终端窗口中终止,则-t选项将返回true(0)。我们已经使用文件描述符0作为测试的参数,它表示" stdin"。

如果将" stdin"连接到终端窗口,则测试将证明是正确的。如果stdin连接到文件或者管道,则测试将失败。

我们可以使用任何方便的文本文件来生成脚本的输入。在这里,我们使用一个叫做dummy.txt的文件。

./input.sh < dummy.txt

输出显示脚本识别出输入不是来自键盘,而是来自文件。如果选择这样做,则可以相应地更改脚本的行为。

那是与文件重定向有关的,让我们通过管道进行尝试。

cat dummy.txt | ./input.sh

该脚本识别出其输入正在通过管道传递到其中。更确切地说,它再次识别出" stdin"流未连接到终端窗口。

让我们既不使用管道也不使用重定向来运行脚本。

./input.sh

" stdin"流连接到终端窗口,脚本相应地报告该情况。

要在输出流中检查同一件事,我们需要一个新脚本。在编辑器中键入以下内容,并将其另存为output.sh。

#!/bin/bash

if [ -t 1 ]; then

echo stdout is going to the terminal window
 
else

echo stdout is being redirected or piped
 
fi

使用以下命令使其可执行:

chmod +x input.sh

对该脚本的唯一重大更改是在方括号中的测试中。我们使用数字1表示" stdout"的文件描述符。

让我们尝试一下。我们将通过cat传递输出。

./output | cat

该脚本认识到其输出没有直接进入终端窗口。

我们还可以通过将输出重定向到文件来测试脚本。

./output.sh > capture.txt

没有输出到终端窗口,我们默默地返回到命令提示符。正如我们所期望的。

我们可以查看capture.txt文件内部,以查看捕获的内容。使用以下命令进行操作。

cat capture.sh

再次,我们脚本中的简单测试检测到" stdout"流没有直接发送到终端窗口。

如果我们运行脚本时没有任何管道或者重定向,它应该检测到" stdout"已直接传递到终端窗口。

./output.sh