stdin
是程序写入其请求以运行进程任务的文件,stdout
是内核写入其输出并由请求该信息的进程访问的文件,stderr
则是所有异常都输入的文件。但是当我打开这些文件以检查它们是否真的存在时,似乎没有任何迹象表明它们确实存在!
我想知道的是,这些文件的确切目的是什么,简单易懂的答案,尽量避免使用技术术语!
标准输入 - 这是您的进程从中读取信息的文件句柄.
标准输出 - 您的进程将常规输出写入此文件句柄.
标准错误 - ���的进程将诊断输出写入此文件句柄。
这基本上是我能够制作的最简化版本 :-)
当然,这主要是按照惯例来做的。如果您愿意,没有什么阻止您将诊断信息写入标准输出。您甚至可以完全关闭这三个文件句柄并打开自己的文件进行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
文件系统也是如此。它们不是真实的文件,只是对内核信息进行严格控制的网关。
stdin
、stdout
和stderr
是"I/O流"而不是文件。正如您所注意到的,这些实体并不存在于文件系统中。但是Unix哲学在I/O方面是"一切皆文件",事实上意味着您可以使用相同的库函数和接口(printf
、scanf
、read
、write
、select
等),无需担心I/O流连接到键盘、磁盘文件、套接字、管道或其他I/O抽象。stdin
、stdout
和stderr
作为编程便利已经预定义好了。这只是一个约定,并不是由操作系统强制执行的。echo
程序为例:echo 'foo' > target.txt
。通常,echo将其输出发送到标准输出(stdout),而在这里我们将输出重定向到target.txt
文件中。 - Niels Bomecho
的标准输入交互。事实上,如果您将 echo
替换为一个 读取 标准输入的程序,它只会坐在那里等待您的终端输入。在这种情况下,hello
作为参数提供(通过 argc/argv
)。关于 2>&1
和 &>
具有相同效果的评论是准确的,如果您将效果视为所述的:“将 stderr 与 stdout 合并”。它们都可以做到这一点,但方式略有不同。等价性将是 > somefile 2>&1
和 &> somefile
。 - paxdiablocat /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
是误导性的。
stderr
还应用于那些旨在为运行命令的用户提供信息而不是任何潜在下游数据的消费者的信息性消息(例如,如果您运行一系列命令的shell管道,您不希望像“获取项目 30 of 42424”这样的信息性消息出现在stdout
上,因为它们会让消费者感到困惑,但您可能仍然希望用户看到它们)。
参见此文以了解历史上的理由:
“所有程序都将诊断信息放在标准输出中。当输出被重定向到文件时,这常常引起问题,但当输出被发送到不知情的进程时,这种情况变得无法忍受。尽管如此,人们还是不愿违反标准输入标准输出模型的简单性,经过v6时期的容忍,直到Dennis Ritchie引入标准错误文件来剪断Gordian结。不过这还不够。在管道中,诊断信息可能来自同时运行的多个程序。诊断信息需要标识自己。”
从控制台读取输入(例如键盘输入)。 在C语言中与scanf一起使用。
scanf(<formatstring>,<pointer to storage> ...);
将内容输出到控制台。在 C 语言中使用 printf 函数。
printf(<string>, <values to print> ...);
将“错误”输出发送到控制台。 在C中与fprintf一起使用。
fprintf(stderr, <string>, <values to print> ...);
标准输入的来源可以被重定向。例如,它不一定来自于键盘输入,可以来自文件(echo < file.txt
)或另一个程序 (ps | grep <userid>
).
标准输出和标准错误也可以被重定向。例如,标准输出可以被重定向到文件:ls . > ls-output.txt
,在这种情况下,输出会被写入到文件ls-output.txt
。标准错误可以用2>
进行重定向。
echo < file.txt
是行不通的。因为 echo
命令接受字符串作为参数,而不是读取标准输入。 - ordem/proc/(pid)/fd/0 - 标准输出文件
/proc/(pid)/fd/1 - 标准输入文件
/proc/(pid)/fd/2 - 标准错误文件
例如
但是,只有/bin/bash工作得很好,其他进程通常在0中没有任何内容,但在2中写入了许多错误信息。
想要获取关于这些文件的权威信息,请查看man页面,在终端中运行命令即可。
$ man stdout
简单来说,每个文件都用于:
stdout 输出流
stdin 输入流
stderr 打印错误或日志信息。
每个 Unix 程序都有这些流中的每一个。
stderr不会进行IO缓存,因此如果我们的应用程序需要将关键信息(例如错误、异常)打印到控制台或文件中,请使用它,而使用stdout打印一般日志信息,因为它使用IO缓存,存在一种情况,即在将消息写入文件之前,应用程序可能会关闭,从而使调试变得复杂。
具有关联缓冲区的文件称为流,声明为指向已定义类型FILE的指针。fopen() 函数会为流创建某些描述性数据,并返回一个指针以在所有后续交易中指定该流。通常,在头文件中声明了三个具有恒定指针的打开流,它们与标准打开文件相关联。
在程序启动时,预定义了三个流,无需显式打开:标准输入(用于读取传统输入)、标准输出(用于写入传统输出)和标准错误(用于写入诊断输出)。当打开标准错误流时,它不是完全缓冲的;仅当可以确定流不引用交互式设备时,标准输入和标准输出流才是完全缓冲的。
/dev/stdin
是一个符号链接,指向当前正在运行的程序打开的第一个文件描述符/proc/self/fd/0
。因此,由/dev/stdin
指向的内容会因程序而异,因为/proc/self/
总是指向“当前正在运行的程序”。(即执行open
调用的任何程序。)/dev/stdin
和其他类似的文件是为了让 setuid shell 脚本更加安全,并可以将文件名/dev/stdin
传递给仅处理文件的程序,但您需要进行更多交互控制。(有一天这将是你需要知道的一个有用技巧。 :) - sarnold