对stdin, stdout和stderr感到困惑?

我对这三个文件的用途感到相当困惑。如果我的理解正确的话,stdin是程序写入进程中运行任务的请求的文件,stdout是内核写入输出和访问请求信息的进程的文件,而stderr是所有异常都输入的文件。在打开这些文件来检查这些是否真的发生了,我发现似乎没有什么建议!

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

311471 次浏览

标准输入——这是你的进程从你那里获取信息的文件句柄

标准输出 -你的进程写入常规输出到这个文件句柄。

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

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

当然,这主要是惯例。如果您愿意,没有什么可以阻止您将诊断信息写入标准输出。您甚至可以完全关闭三个文件句柄,并打开您自己的文件进行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文件系统也是如此。这些不是真正的文件,只是严格控制的通往内核信息的网关。

更准确地说,stdinstdoutstderr是“I/O流” 比文件。正如您所注意到的,这些实体并不存在于文件系统中。但是, Unix的哲学,就I/O而言,是“一切都是一个文件”。在实践中, 这实际上意味着你可以使用相同的库函数和接口(printfscanfreadwriteselect等),而不用担心是否有I/O流 连接到键盘、磁盘文件、套接字、管道或其他一些I/O抽象 大多数程序需要读取输入,写入输出,并记录错误,因此stdinstdout, 和stderr是为你预定义的,为了编程方便。这只是

. b0 . b0 . b0 . b0 . b0 . b0 . b0 . b0 . b0 . b0 . b0

stdin

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

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

stdout

输出到控制台。 在C中使用printf

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

stderr

向控制台输出“错误”。 在C中使用fprintf

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

重定向

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

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

恐怕你的理解完全落后了。:)

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

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

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

当文件需要读取输入时,使用"standard in",使用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,并将每个程序的标准输出连接到管道中下一个程序的标准输入。

其次,我给出了用于处理文件流的标准ISO C函数(FILE *对象)——在内核级别,它都是文件描述符(int对文件表的引用)和更低级的操作,如readwrite,它们不能像ISO C函数那样进行愉快的缓冲。我想保持简单,使用更简单的函数,但我认为你仍然应该知道替代方案。:)

使用ps -aux显示当前进程,所有这些进程都列在/proc/ as /proc/(pid)/中,通过调用cat /proc/(pid)/fd/0,它会打印在该进程的标准输出中找到的任何东西。因此,或许,

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

. b

例如my terminal window

但只有在/bin/bash中才能很好地工作,其他进程通常在0中没有任何内容,但在2中有许多错误

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

它还应该用于为运行命令的用户提供信息的消息,而不是用于数据的任何潜在下游消费者(例如,如果您运行一个连接多个命令的shell管道,您不需要像“getting item 30 of 42424”这样的信息消息。出现在stdout上,因为它们会让消费者感到困惑,但你可能仍然希望用户看到它们。

历史原理参见:

所有程序都将诊断放在标准输出上。这 当输出被重定向到文件时总是会引起麻烦,但是 当输出被发送给一个不知情的人时,变得不可容忍 的过程。尽管如此,不愿违背单纯 标准输入-标准输出模式,人们容忍了这种状态 v6的事务。此后不久,丹尼斯·里奇(Dennis Ritchie)剪掉了《戈尔迪安》 通过引入标准错误文件。这还不够。 对于管道,诊断可以来自几个程序中的任何一个 同时运行。诊断需要自我识别。"

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

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

stderr将不做IO缓存缓冲,所以如果我们的应用程序需要打印关键消息信息(一些错误,异常)到控制台或文件使用它作为使用标准输出打印一般日志信息,因为它使用IO缓存缓冲有一个机会,在写入我们的消息到文件应用程序可能会关闭,留下复杂的调试

作为上述答案的补充,以下是关于重定向的总结: Redirections cheatsheet < / p >

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

第一个例子根本没有使用stdin,它传递的是“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

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

$ man stdout

但简单来说,每个文件都是用于:

stdout用于流输出

stdin用于流输入

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

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