在Linux下,当一个用户进程被创建的时候,系统会自动为该进程创建三个数据流,也就是题目中所提到的这三个。
1.三个数据流默认是表现在用户终端上的
执行一个shell命令行时通常会自动打开三个标准文件:
- 标准输入文件(stdin),通常对应终端的键盘;
- 标准输出文件(stdout)和标准错误输出文件(stderr),这两个文件都对应终端的屏幕。
进程将从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。
如下例所示,cat命令的功能是从命令行给出的文件中读取数据,并将这些数据直接送到标准输出。
//cat命令行含参数:把文件config的内容依次显示到屏幕上
[root@localhost ~]# cat config
//cat命令行没有参数:会从标准输入中读取数据,并将其送到标准输出。
#用户输入的每一行都立刻被cat命令输出到屏幕上,直到按Ctrl+d结束【标准输入接收到EOF(结束)标识符】
[root@localhost ~]# cat
Hello world
Hello world
Bye
Bye
终端输入输出存在问题:
输入数据从终端输入时,输入的数据只能用一次,且输入有误修改起来不是很方便。
输出到终端屏幕上的信息只能看不能动。无法对此输出作更多处理。
为了解决上述问题,Linux系统为输入、输出的传送引入了另外两种机制,即输入/输出重定向和管道。
stdin, stdout, stderr – standard I/O streams,stdout和stderr都是标准输出,只是stream不同。stdout就是正常的终端打印输出,而stderr是错误信息。 从终端上看,二者并无直观分别。 但是,由于stream不同,在进行组合操作时,会有较大差异。
作为Unix标准的一部分,这哥俩一开始是从C语言出来的。 其它更早的语言,虽然也有区分正常打印与错误信息的,比如Fortran。 但是,stdout和stderr这两个词,直接出自C语言的stdio.h
。
#include<stdio.h>
int main(void)
{
printf("stdout\n");
fprintf(stdout, "stdout\n");
fprintf(stderr, "stderr\n");
return 0;
}
以上C语言代码编译后运行,会在终端显示三行。 两行stdout
会打印到stdout,一行stderr
会打印到stderr。
2.三个数据流可以重定向到文件中
(1)输入重定向
输入重定向是指把命令(或可执行程序)的标准输入重定向到指定的文件中。也就是说,输入可以不来自键盘,而来自一个指定的文件。
使用“ < ”符号将标准输入重定向到文件中
如下例所示,wc将返回该文件所包含的行数、单词数和字符数。
//把wc命令的输入重定向为/etc/passwd文件
[root@localhost ~]# wc < /etc/passwd
24 30 1061
(2)输出重定向
输出重定向是指把命令(或可执行程序)的标准输出或标准错误输出重新定向到指定文件中。这样,该命令的输出就不显示在屏幕上,而是写入到指定文件中。
使用 “ > ”符号,将标准输出重定向到文件中。形式为:命令>文件名
使用“ >> ”符号,将标准输出结果追加到指定文件后面。形式为:命令>>文件名
使用“ 2> ”符号,将标准错误输出重定向到文件中。形式为:命令 2> 文件名
使用“ 2>> ”符号,将标准错误输出追加到指定文件后面。形式为:命令 2>>文件名
使用“ 2>&1 ”符号或“ &> ”符号,将把标准错误输出stderr重定向到标准输出stdout
使用“ >/dev/null ”符号,将命令执行结果重定向到空设备中,也就是不显示任何信息。
[root@localhost ~]# ls > directory.out
[root@localhost ~]# ls *.doc>>directory.out
[root@localhost ~]# ls /usr/tmp 2> err.file //可在屏幕上看到程序的正常输出结果,但又将程序的任何错误信息送到文件err.file中,以备将来检查用
[root@localhost ~]# ls /usr/tmp &> output.file
[root@localhost ~]# ls /usr/tmp 2>&1 output.file
[root@localhost ~]# ls >/dev/null
默认情况下,echo
就是打印到stdout:
echo "hello"
如果需要打印到stderr,则要麻烦一些:
echo "error" 1>&2
其中,末尾的1
可以省略,可以写作>&2
。 这里,1
就代表stdout,2
就代表stderr。 含义就是,把stdout的输出内容,重定向(redirect to)到stderr中去。
前面1>&2
的表达式,就是把stdout里的内容,重定向到stderr中。 1
可以省略,是因为>
符号前面,默认是1
,也就是指>
默认只操作stdout。
同理,2>&1
就是把stderr里的内容,重定向到stderr中。 这个更常用一些。
这种表达式,不仅对echo
生效,而是对任何Bash调用都生效,包括自定义脚本与程序。
注意:2>&1
这类表达式中间不能有空格。
有时需要把终端里的输出,重定向到一个文件中去。 比如,把make
的编译过程,重定向至build.log
文件中。
make > build.log
//相当于
make 1> build.log
这时,stdout的内容不再打印,而是重定向到build.log
文件中。 而在终端里,仍然有可能会有stderr的输出。 有时,这正是俺们需要的。
有些时候,俺们的需求是相反的。 不显示任何stderr的输出,只显示stdout的正常输出。
make 2> /dev/null
同时重定向stdout与stderr,有两种方法。
- 先把stderr重定向到stdout中,然重定向stdout到文件
- 直接把stdout和stderr都重定向到文件
一般网上流行的是第一种方法。
make > build.log 2>&1
这种方法,不仅逻辑上麻烦,而且很容易出错。 例如,有时会写成make 2>&1 > build.log
,这是无效的,与make > build.log
等价。 2>&1
必须要放到一个完整Bash表达式的最后,才能生效。 这一点,非常容易出错。
第二种方法,直接把stdout和stderr都重定向到文件。 不仅形式简单,而且不会出错。
make &> build.log
把&>
替换成>&
,也是等价的。 另外要注意,这种用法仅在Bash中有效,在标准的sh无效,其它shell则没试过。 而1>
、2>
、2>&1
即使在标准的sh中,也是有效的。
有时既需要打印,又需要重定向。 还是以make
举例,俺们通常既需要看到编译的过程,又需要分析build.log
。 这时,tee
就可以满足需求。
make | tee build.log
这一句的意思是,把make
的stdout输出,传给tee
; 而tee
则会打印到stdout的同时,重定向到build.log
中。 所以结果是,stdout和stderr都会被打印,而只有stdout才会被重定向到build.log
中。
这是不是不合用? 通常,俺们需要完整的结果作为log。 如果只有stdout,反而找不出真正的问题。
make 2>&1 | tee build.log
这一句就满足了保存完整log的需求。
咦?等等! 那个2>&1
的位置,似乎有些奇怪?
这是因为,管道|
分割了Bash表达式,这一句实际上是两个表达式。 所以,2>&1
确实是在表达式的末尾,没错。
如果换成make | tee build.log 2>&1
,反而无法生效。 因为,管道|
只会传递stdout的输出,不会传递stderr的输出。
其他用法:
cmd > /dev/null //丢弃stdout的打印输出,通常是希望终端输出干净一些。 有错误就说话,没错就什么也别说——这也是符合Unix哲学的一种设计
cmd 2> /dev/null //丢弃stderr的打印输出,不想看到stderr,只要stdout,这通常是在一个脚本里组合多个命令时使用。
cmd &> /dev/null //丢弃所有输出,把Unix哲学贯彻到底!
cmd 1> stdout.log 2> stderr.log //分别重定向stdout和stderr,比较常用的日志转存策略,写1>而不是>,略微清楚一些
cmd 2> stderr.log | tee stdout.log //打印并重定向stdout,重定向stderr,要注意,由于管道|与tee的特性,反过来无法做到。 并且,同时打印并重定向stdout与stderr,也无法做到。 出现这两种情况,只能让cmd自己去解决。
(3)几个基本符号及其含义
- /dev/null 表示空设备文件
- 0 表示stdin标准输入
- 1 表示stdout标准输出
- 2 表示stderr标准错误
stdin,stdout和stderr还是和终端有密切关系,通常在生产环境时,会将这3个流重定向到其它文件。比如编写守护进程的时候,因为守护进程和终端无关,所以往往会将stdin,stdout和stderr重定向到/dev/null去。
在Linux系统中,自行查看相关帮助文档:
man stdout
man echo
man tee
man null
3.stdout和stderr区别
我们知道,标准输出和标准错误默认都是将信息输出到终端上,那么他们有什么区别呢?让我们来看个题目:
问题:下面程序的输出是什么?(intel笔试2011)
int main(){
fprintf(stdout,"Hello ");
fprintf(stderr,"World!");
return0;
}
解答:这段代码的输出是什么呢?你可以快速的将代码敲入你电脑上(当然,拷贝更快),然后发现输出是
World!Hello
原理:在默认情况下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。
而stderr是无缓冲的,会直接输出,举例来说就是printf(stdout, “xxxx”) 和 printf(stdout, “xxxx\n”),前者会憋住,直到遇到新行才会一起输出。而printf(stderr, “xxxxx”),不管有么有\n,都输出。
参考文档:
Average Rating