最近写shell脚本写得心碎,今天就遇到了一个哭笑不得的问题,特地分享给大家。
我们都知道shell脚本中有三个标准IO文件,0表示标准输入,1表示标准输出,2表示错误输出。0通常连接到键盘,1和2通常连接到显示器。因此我们可以通过键盘输入,通过显示器看到标准输出和错误输出。如果我们想把错误输出单独重定向到文件error.txt中,可以这样做:
# test.sh
let 1/0
$ bash test.sh 2> error.txt
用1除以0显然会报错,错误信息被重定向到了error.txt中,可以用cat命令查看:
$ cat error.txt
test.sh:行1: let: 1/0: 除0 (错误符号是 "0")
以上用法应该是大家习以为常的做法,结果合乎情理。然而今天当我想要把一个程序的错误输出重定向到error.txt中时却出现了意想不到的现象。过程如下:
// test.cpp
int main()
{
int num = 1 / 0;
return 0;
}
$ g++ test.cpp -o test
$ ./test 2> error.txt
浮点数例外(吐核)
错误输出竟然没有重定向到error.txt,而是直接输出在了屏幕上。查看error.txt的内容,什么都没有。
这个现象让我郁闷了很久,为什么C++程序出错和shell脚本出错的表现会有不同。
机智的我突然想到,python脚本会不会也有这个问题呢,遂尝试了一下:
# test.py
print 1/0
$ python test.py 2> error.txt
结果屏幕什么都没打印,查看error.txt可以看到
$ cat error.txt
Traceback (most recent call last):
File "test.py", line 1, in <module>
print 1/0
ZeroDivisionError: integer division or modulo by zero
所以,python脚本出错的表现与shell脚本相同。就因为它们都是脚本???
这里面一定有不可告人的秘密。然而我想不出来...
只好求助网友,看看下面这位大佬怎么说的。
看了他的回答简直醍醐灌顶!在我看来如此怪异的现象偏偏是合情合理的。问题就出在C++程序和shell脚本以及python脚本的差别,也就是编译型语言和解释型语言(或称为脚本语言)的差别。编译型语言编译后的可执行程序可以直接由操作系统调用执行,而解释型语言的执行必须由解释器解释执行。这就导致了一个问题,编译型语言中的错误只能由调用它的操作系统来捕获并打印错误信息,而解释型语言中的错误可以由它的解释器捕获并打印错误信息。也就是说,./test
本身的输出并不包含错误信息,而python test.py
本身的输出就已经包含了错误信息。恰好command 2> error.txt
这样的用法是对command命令自身的输出进行重定向,这就解释了为什么前者的错误无法被重定向,而后者的错误可以被重定向。
大佬同时给出了可以满足我要求的用法:
$ ( ./test ) 2> error.txt
在要运行的命令外面加括号,可以使该命令在子shell中执行。子shell作为test程序的容器,可以捕获内部命令抛出的异常,并作为自己的输出,从而可以将错误输出重定向到error.txt中。
搞清楚了这个问题,顿时感觉神清气爽!可以下班喽~
参考资料
Why an executable running in shell output to stdout instead of stderr when crashes? stackoverflow