对标准输入、标准输出和标准错误感到困惑?

Posted

技术标签:

【中文标题】对标准输入、标准输出和标准错误感到困惑?【英文标题】:Confused about stdin, stdout and stderr? 【发布时间】:2011-03-24 01:08:53 【问题描述】:

我对这三个文件的用途感到很困惑。如果我的理解是正确的,stdin 是程序写入其请求以在进程中运行任务的文件,stdout 是内核写入其输出的文件,请求它的进程从, stderr 是输入所有异常的文件。在打开这些文件以检查这些文件是否确实发生时,我发现似乎没有任何建议!

我想知道这些文件的确切用途是什么,用很少的技术术语绝对愚蠢的回答!

【问题讨论】:

【参考方案1】:

标准输入 - 这是您的进程读取的文件句柄以从您那里获取信息。

标准输出 - 您的进程将传统输出写入此文件句柄。

标准错误 - 您的进程将诊断输出写入此文件句柄。

这是我能做到的最愚蠢的事情了:-)

当然,这主要是按照惯例。如果您愿意,没有什么能阻止您将诊断信息写入标准输出。您甚至可以完全关闭三个文件句柄并打开自己的文件进行 I/O。

当您的进程启动时,它应该已经打开了这些句柄,并且可以读取和/或写入它们。

默认情况下,它们可能已连接到您的终端设备(例如,/dev/tty),但 shell 将允许您在这些句柄与特定文件和/或设备(甚至与其他进程的管道)之间建立连接你的进程开始了(一些可能的操作相当聪明)。

一个例子是:

my_prog <inputfile 2>errorfile | grep XYZ

这将:

my_prog 创建一个进程。 打开 inputfile 作为标准输入(文件句柄 0)。 打开 errorfile 作为标准错误(文件句柄 2)。 为grep创建另一个进程。 将my_prog的标准输出附加到grep的标准输入。

你的评论:

当我在 /dev 文件夹中打开这些文件时,为什么我永远看不到正在运行的进程的输出?

这是因为它们不是普通文件。虽然 UNIX 将 everything 呈现为文件系统中某处的文件,但在最低级别并没有做到这一点。 /dev 层次结构中的大多数文件是字符设备或块设备,实际上是设备驱动程序。它们没有大小,但有主要和次要设备号。

当您打开它们时,您将连接到设备驱动程序而不是物理文件,并且设备驱动程序足够智能,知道应该单独处理单独的进程。

Linux /proc 文件系统也是如此。这些不是真正的文件,只是内核信息受到严格控制的网关。

【讨论】:

这是您的回复。虽然我可以从您描述的内容中理解文件的目的,但我想更进一步。当我在 /dev 文件夹中打开这些文件时,为什么我永远看不到正在运行的进程的输出。假设我在终端上执行 top ,它是否不应该定期将其结果输出到 stdout 文件,因此当它被更新时,我应该能够看到输出的实例被打印到这个文件上。但事实并非如此.. 这些文件也不一样(/dev 目录中的那些)。 因为从技术上讲,这些不是文件。它们是设备节点,表示要写入的特定设备。 UNIX 可以将所有内容呈现为文件抽象,但在最深层次上并没有做到这一点。 使用 shell 重定向功能。 xyz &gt;xyz.out 会将您的标准输出写入一个物理文件,其他进程可以读取该文件。 xyz | grep something 将更直接地将xyz 标准输出连接到grep 标准输入。如果您希望不受限制地访问您无法以这种方式控制的进程,则需要查看/proc 之类的内容,或者编写代码以通过某种方式连接到内核来过滤输出。可能还有其他解决方案,但它们都可能彼此一样危险:-) @Shouvik,注意/dev/stdin 是指向/proc/self/fd/0 的符号链接——当前正在运行的程序打开的第一个文件描述符。因此,/dev/stdin 所指向的内容会因程序而异,因为/proc/self/ 始终指向“当前正在运行的程序”。 (无论哪个程序都在执行open 调用。)/dev/stdin 和朋友们被放在那里以使 setuid shell 脚本更安全,并让您将文件名 /dev/stdin 传递给仅处理文件的程序,但您想控制更多交互式地。 (总有一天这会成为你知道的一个有用的技巧。:) @Oswin,已将其更改为使用 ISO 标准中的术语(常规和诊断),因为这是确定的来源。【参考方案2】:

更正确的说法是 stdinstdoutstderr 是“I/O 流”而不是 比文件。正如您所注意到的,这些实体并不存在于文件系统中。但是 就 I/O 而言,Unix 哲学是“一切都是文件”。在实践中, 这实际上意味着您可以使用相同的库函数和接口 (printf, scanfreadwriteselect等)无需担心I/O流是否 连接到键盘、磁盘文件、套接字、管道或其他一些 I/O 抽象。

大多数程序需要读取输入、写入输出和记录错误,所以stdinstdout、 和stderr 是为您预定义的,以方便编程。这只是 一种约定,操作系统不强制执行。

【讨论】:

感谢您的意见。您是否知道我如何截取进程的输出数据流并将其输出到我自己的文件中?【参考方案3】:

作为上述答案的补充,以下是关于重定向的总结:

编辑:这个图形并不完全正确。

第一个示例根本不使用标准输入,它将“hello”作为参数传递给 echo 命令。

图中还说 2>&1 与 &> 具有相同的效果

ls Documents ABC > dirlist 2>&1
#does not give the same output as 
ls Documents ABC > dirlist &>

这是因为 &> 需要重定向到一个文件,而 2>&1 只是将 stderr 发送到 stdout

【讨论】:

您的评论与接受的答案相结合非常有意义,并且清楚地解释了事情!谢谢! 一张图抵千言! &> 需要文件重定向到,但 2>&1 不 &> 用于记录,但 2>&1 可同时用于记录和终端 STDOUT STDERR 视图 2> &1 仅用于查看程序的 STDOUT 和 STDERR(在终端的 command_prompt 中)(取决于程序处理错误的能力) 实际上,第一个是完全错误的。 “hello”绝不会与echo 的标准输入交互。事实上,如果您将echo 替换为读取 标准输入的程序,它就会坐在那里等待您的终端输入。在这种情况下,hello 作为参数提供(通过argc/argv)。如果您看到如下所述的效果,那么关于 2&gt;&amp;1&amp;&gt; 具有相同效果的评论是准确的:“将标准错误与标准输出合并”。他们都这样做,但方式略有不同。等价的将是&gt; somefile 2&gt;&amp;1&amp;&gt; somefile 您最好将图形重写为文本,这样您就可以轻松删除第一个 :-)【参考方案4】:

恐怕你的理解完全倒退了。 :)

程序的角度考虑“标准输入”、“标准输出”和“标准错误”,而不是从内核的角度。

当程序需要打印输出时,它通常会打印到“标准输出”。程序通常使用printf 将输出打印到标准输出,而printf 只会打印到标准输出。

当程序需要打印错误信息(不一定是异常,那些是编程语言结构,强加于更高级别)时,它通常会打印到“标准错误”。它通常使用fprintf 执行此操作,它接受打印时使用的文件流。文件流可以是任何为写入而打开的文件:标准输出、标准错误或任何其他使用fopenfdopen 打开的文件。

当文件需要读取输入时使用“标准输入”,使用freadfgetsgetchar

这些文件中的任何一个都可以从 shell 轻松重定向,如下所示:

cat /etc/passwd > /tmp/out     # redirect cat's standard out to /tmp/foo
cat /nonexistant 2> /tmp/err   # redirect cat's standard error to /tmp/error
cat < /etc/passwd              # redirect cat's standard input to /etc/passwd

或者,整个墨西哥卷饼:

cat < /etc/passwd > /tmp/out 2> /tmp/err

有两个重要的警告:首先,“标准输入”、“标准输出”和“标准错误”只是一个约定。它们是一个非常强大的约定,但这只是一个协议,能够运行这样的程序非常好:grep echo /etc/services | awk 'print $2;' | sort 并将每个程序的标准输出连接到标准输入流水线中的下一个程序。

其次,我给出了用于处理文件流(FILE * 对象)的标准 ISO C 函数——在内核级别,它是所有文件描述符(int 对文件表的引用),而且要低得多像readwrite 这样的级别操作,它们不能很好地缓冲ISO C 函数。我想保持简单并使用更简单的功能,但我认为你应该知道替代方案。 :)

【讨论】:

当进程被执行时,它会将错误写入这个stderr文件,或者当程序从它的源代码编译时。另外,当我们从编译器的角度谈论这些文件时,它与说程序相比有什么不同吗? @Shouvik,编译器只是另一个程序,有自己的标准输入、标准输出和标准错误。当编译器需要写入警告或错误时,它会将它们写入stderr。当编译器前端为汇编器输出中间代码时,它可能会在 stdout 上编写中间代码,而汇编器可能会在 stdin 上接受其输入,但从您作为用户的角度来看,这一切都将在幕后发生。)编译的程序,该程序也可以将错误写入其标准错误,但与编译无关。 感谢您提供的信息。我想我很愚蠢,无论如何都不从那个角度看待它......:P 所以你是说标准可以帮助我们打印程序【参考方案5】:

我认为人们说 stderr 应该只用于错误消息是误导性的。

它也应该用于为运行命令的用户而不是任何潜在的数据下游消费者提供的信息性消息(即,如果您运行一个链接多个命令的 shell 管道,您不想要信息性消息,例如“getting 42424 中的第 30 项”出现在 stdout 上,因为它们会使消费者感到困惑,但您可能仍希望用户看到它们。

请参阅 this 了解历史原因:

“所有程序都在标准输出上放置了诊断信息。这有 当输出被重定向到一个文件时总是会引起麻烦,但是 当输出被发送到一个毫无戒心的人时变得无法容忍 过程。然而,不愿意违反简单的 标准输入标准输出模型,人们可以容忍这种状态 事务通过 v6。此后不久,丹尼斯·里奇砍下了戈迪安 通过引入标准误差文件来打结。这还不够。 使用管道诊断可以来自多个程序中的任何一个 同时运行。需要通过诊断来识别自己。”

【讨论】:

历史原因链接已断开 - 域已消失! 替换为archive.org链接。【参考方案6】:

标准输入

通过控制台读取输入(例如键盘输入)。 在 C 中与 scanf 一起使用

scanf(<formatstring>,<pointer to storage> ...);

标准输出

产生到控制台的输出。 在 C 中与 printf 一起使用

printf(<string>, <values to print> ...);

标准错误

向控制台生成“错误”输出。 在 C 中与 fprintf 一起使用

fprintf(stderr, <string>, <values to print> ...);

重定向

标准输入源可以重定向。例如,它可以来自文件(echo &lt; file.txt)或另一个程序(ps | grep &lt;userid&gt;),而不是来自键盘输入。

stdout、stderr 的目的地也可以被重定向。例如,stdout 可以重定向到一个文件:ls . &gt; ls-output.txt,在这种情况下,输出被写入文件ls-output.txt。 Stderr can be redirected 和 2&gt;

【讨论】:

【参考方案7】:

使用 ps -aux 显示当前进程,所有这些都列在 /proc/ 作为 /proc/(pid)/,通过调用 cat /proc/(pid)/fd/0 它打印在我认为该过程的标准输出。所以也许,

/proc/(pid)/fd/0 - 标准输出文件 /proc/(pid)/fd/1 - 标准输入文件 /proc/(pid)/fd/2 - 标准错误文件

例如

但只对 /bin/bash 有效,其他进程通常在 0 中没有任何内容,但许多在 2 中写入错误

【讨论】:

【参考方案8】:

有关这些文件的权威信息,请查看手册页,在终端上运行命令。

$ man stdout 

但对于一个简单的答案,每个文件都是为了:

stdout 用于流输出

stdin 用于流输入

stderr 用于打印错误或日志消息。

每个 unix 程序都有这些流中的每一个。

【讨论】:

【参考方案9】:

stderr 不会进行 IO 缓存缓冲,因此如果我们的应用程序需要打印关键消息信息(一些错误、异常)到控制台或文件,请使用它,因为使用 stdout 打印一般日志信息,因为它使用 IO 缓存缓冲有在将我们的消息写入文件应用程序之前可能会关闭,从而使调试变得复杂

【讨论】:

【参考方案10】:

具有关联缓冲的文件称为流,并被声明为指向已定义类型 FILE 的指针。 fopen() 函数为流创建某些描述性数据,并返回一个指针以指定所有后续事务中的流。通常有三个打开的​​流,它们在标头中声明并与标准打开文件相关联的常量指针。 在程序启动时,三个流是预定义的,不需要显式打开:标准输入(用于读取常规输入)、标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。打开时,标准错误流没有完全缓冲;当且仅当可以确定流不引用交互式设备时,标准输入和标准输出流才会被完全缓冲

https://www.mkssoftware.com/docs/man5/stdio.5.asp

【讨论】:

【参考方案11】:

这是一篇关于stdinstdoutstderr 的长文:

What Are stdin, stdout, and stderr on Linux?

总结一下:

像文件一样处理流

Linux 中的流(几乎与其他所有内容一样)被视为 它们是文件。您可以从文件中读取文本,也可以编写文本 成一个文件。这两个动作都涉及数据流。所以 将数据流作为文件处理的概念并没有那么复杂 拉伸。

与进程关联的每个文件都分配有一个唯一编号 识别它。这称为文件描述符。每当一个动作 需要对文件执行,文件描述符用于 识别文件。

这些值始终用于标准输入、标准输出和标准错误:

0: stdin
1: stdout
2: stderr

具有讽刺意味的是,我在堆栈溢出和上面的文章中发现了这个问题,因为我正在搜索有关异常/非标准流的信息。所以我的搜索还在继续。

【讨论】:

以上是关于对标准输入、标准输出和标准错误感到困惑?的主要内容,如果未能解决你的问题,请参考以下文章

Shell标准输入标准输出和标准错误的重定向总结

shell脚本中常见的标准输入标准输出标准错误重定向详解

在 Eclipse 中分离标准输入和标准输出文件

对截断语句的标准定义感到困惑

Linux下Shell重定向

文件IO详解---标准输入标准输出和标准错误