对stdin,stdout和stderr感到困惑?


230

我对这三个文件的目的感到困惑。如果我的理解是正确的,stdin则是程序在其中写入其在进程中运行任务的请求stdout的文件,是内核在其中写入其输出以及请求其从中访问信息的进程stderr的文件,并且是该文件。输入所有例外。在打开这些文件以检查它们是否确实发生时,我发现似乎没有任何暗示!

我想知道的是这些文件的目的是什么,用很少的技术术语就完全可以解决问题了!


36
观察:这个问题在2010年是可以接受的,但如今会很快被否决。
byxor

3
@布兰登你能提供一个理由吗?我认为这对您的评论很有价值。
独立

3
@byxor是公平的,我会问:op的帖子是否在要求人们帮助他调试代码?Shouvik似乎问了一个关于stdin,stdout和stderr的目的的问题。操作员的职位似乎出于好奇,不是吗?(我实际上是在了解自己的自动
取款机

2
@ user123456您是正确的。那时,我正在学习成为软件开发人员和S / O,这是学习编程的好地方。我们原本打算将其作为针对所有计算科学问题的Wiki服务。#juniorDevForLife
Shouvik '17

3
@Shouvik感谢您的那段历史。我也在学习如何成为一名软件开发人员(刚刚被科幻小说中的一个酷派所接受)。我对S / O还是很陌生,仍然不确定我可以和不能张贴什么。我发现这里的节制可能非常严格。我喜欢那个井号。#juniorDevForLife。我会在这里代替您发表评论,因为这不会增加讨论的任何内容,但是我不认为S / O具有pm系统。祝你有美好的一天。
sansae

Answers:


251

标准输入 -这是您的过程读取以获取您的信息文件句柄

标准输出 -您的进程将正常信息写入此文件句柄。

标准误差 -您的进程将错误信息写入此文件句柄。

这就是我所能做到的愚蠢:-)

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

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

默认情况下,它们可能已连接到您的终端设备(例如, /dev/tty),但是通过Shell,您可以在进程开始之前(这些进程中的某些可能的操作相当聪明)。

一个例子是:

my_prog <inputfile 2>errorfile | grep XYZ

这将:

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

发表您的评论:

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

这是因为它们不是普通文件。尽管UNIX将所有内容都以文件形式显示在文件系统中的某个位置,但是在最低级别上却并非如此。/dev层次结构中的大多数文件是字符设备或块设备,实际上是设备驱动程序。它们没有大小,但是具有主设备号和次设备号。

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

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


1
多数民众赞成在您的回应中。虽然可以从您的描述中了解文件的用途,但我想进一步介绍一下。当我在/ dev文件夹中打开这些文件时,为什么我再也看不到正在运行的进程的输出。假设我在终端上执行top,是否不应该将其结果定期输出到stdout文件中,因此在对其进行更新时,我应该能够看到输出实例被打印到该文件上。但是,事实并非如此。这些文件也不一样(/ dev目录中的文件)。
Shouvik

7
因为从技术上讲这些不是文件。它们是设备节点,指示要写入的特定设备。UNIX可以所有内容作为文件抽象呈现给您,但是在最深层次上却并非如此。
paxdiablo

1
使用外壳重定向功能。xyz >xyz.out会将您的标准输出写入一个物理文件,其他进程可以读取该文件。xyz | grep somethingxyzstdout grep更直接地连接到stdin。如果您希望不受限制地访问过程,那么就需要研究类似的东西/proc或编写代码以某种方式挂接到内核来过滤输出。可能还有其他解决方案,但它们彼此之间可能同样危险:-)
paxdiablo 2010年

20
@Shouvik,请注意,这/dev/stdin是一个符号链接/proc/self/fd/0-当前正在运行的程序已打开的第一个文件描述符。因此,所指向的内容/dev/stdin将在程序之间变化,因为它/proc/self/始终指向“当前正在运行的程序”。(使用哪个程序进行open调用。)/dev/stdin和朋友一起放在那里,以使setuid shell脚本更安全,并允许您将文件名传递/dev/stdin给仅适用于文件的程序,但您希望以更具交互性的方式进行控制。(总有一天,这将是一个有用的窍门。:)
sarnold 2010年

1
@ CarlosW.Mercado,文件是数据的物理表示。例如,位存储在硬盘上。打开文件句柄后,文件句柄通常是一个小标记,用于引用该文件。
paxdiablo

62

这将是更正确的说stdinstdoutstderr有“I / O流”,而不是文件。您已经注意到,这些实体不存在于文件系统中。但是就I / O而言,Unix的哲学是“一切都是文件”。在实践中,确实意味着你可以使用相同的库功能和接口(printfscanfreadwriteselect,等),而不必担心I / O流是否连接到键盘,磁盘文件,插座,管道,或其他一些I / O抽象。

大多数程序需要读取输入,输出写入和错误日志,所以stdinstdoutstderr预定义的你,作为一个编程方便。这只是一个约定,不能由操作系统强制执行。


感谢您的投入。您是否会知道我如何截取进程的输出数据流并将其输出到我自己的文件中?
Shouvik

51

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

编辑:此图形不是完全正确,但我不确定为什么...

图形显示2>&1与&>具有相同的效果

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

4
您的评论与已接受的答案相结合是很有意义的,并且可以清楚地说明问题!谢谢!
Mykola

1
一张图片胜过千言万语 !
tauseef_CuriousGuy

22

恐怕您的理解是完全落后的。:)

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

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

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

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

这些文件中的任何一个都可以轻松地从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对文件表的引用)以及许多较低级别的操作(例如read和)write,做好ISO C函数的缓冲。我想使其保持简单并使用更简单的功能,但我认为您应该知道所有替代方法。:)


在进程执行过程中将错误写入此stderr文件还是从源代码编译程序时也是如此。同样,当我们从编译器的角度谈论这些文件时,是否与将其与程序进行比较时有所不同?
Shouvik

1
@Shouvik,编译器只是另一个程序,它具有自己的stdin,stdout和stderr。当编译器需要编写警告或错误时,它将把它们写入stderr。当编译器前端为汇编器输出中间代码时,它可能会在stdout上编写中间代码,而汇编器可能会在stdin上接受其输入,但是从用户的角度来看,所有这些都在幕后。)一个已编译的程序,该程序也可以将错误写入其标准错误,但是与编译无关。
sarnold

感谢您提供的信息。我想我还是很愚蠢,无论如何
也不要从

1
因此,您说标准有助于我们打印程序
babygame0ver

9

标准输入

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

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

标准输出

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

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

斯特德

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

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

重新导向

stdin的源可以重定向。例如,它可以来自文件(echo < file.txt)或另一个程序(ps | grep <userid>),而不是来自键盘输入。

stdout,stderr的目的地也可以重定向。例如,stdout可以重定向到文件:ls . > ls-output.txt,在这种情况下,输出将写入文件ls-output.txt标准错误可以重定向2>


8

我认为有人说stderr只应将错误消息用于误导。

它也应该用于提供给运行该命令的用户的信息性消息,而不是给数据的任何潜在下游使用者(例如,如果您运行将多个命令链接在一起的shell管道,则您不希望获得诸如“获取第30条信息”之类的信息性消息)。 42424“出现在屏幕上,stdout因为它们会使消费者感到困惑,但是您可能仍希望用户看到它们。

这个历史的理由:

“所有程序都会对标准输出进行诊断。这总是在将输出重定向到文件时引起麻烦,但是在将输出发送到毫无疑问的过程时就变得无法忍受。但是,不愿意违反标准输入的简单性。在标准输出模型中,人们通过v6容忍了这种状况,此后不久,丹尼斯·里奇(Dennis Ritchie)通过引入标准错误文件削减了戈尔迪诺的结,这还不够。来证明自己。”


3

使用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编写了错误


3

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

$ man stdout 

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

标准输出

输入流的标准输入

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

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


2

stderr不会进行IO缓存缓冲,因此如果我们的应用程序需要打印关键消息信息(某些错误,异常)以进行控制台或文件使用它,那么在使用stdout来打印常规日志信息时(因为它使用IO缓存缓冲),就有机会在将消息写入文件应用程序之前,可能会关闭,从而导致调试复杂


0

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

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

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.