将Python的标准输出重定向到C调用
我在一个Python模块中调用了一个Hello World的可执行文件,它只是简单地在标准输出上打印Hello World
。我想把这个输出重定向到一个Python的StringIO
对象,并且遇到了这个回答,这个回答几乎让我找到了解决方案。
这个回答中关键的部分是以下代码段:
1. def redirect_stdout():
2. print "Redirecting stdout"
3. sys.stdout.flush() # <--- important when redirecting to files
4. newstdout = os.dup(1)
5. devnull = os.open('/dev/null', os.O_WRONLY)
6. os.dup2(devnull, 1)
7. os.close(devnull)
8. sys.stdout = os.fdopen(newstdout, 'w')
另外,我还想在重定向后恢复标准输出,回到之前的状态。
问题
- 上面的函数到底在做什么?
dup
和dup2
在干什么?/dev/null
是什么?- 第8行在做什么?(
sys.stdout = os.fdopen(newstdout, 'w')
)
- 我怎么能把标准输出存储到一个
StringIO
对象中? - 在调用我的Hello World程序之后,我怎么能恢复标准输出?
我很确定,一旦我弄清楚了第一个问题的答案,第二和第三个问题的答案就会变得简单。我决定把这些问题发出来,或许能把第一个问题的答案引向我想要的方向。
3 个回答
可以查看手册页面,了解这些Python函数背后的C语言运行时函数。
简单来说,这些函数的作用是复制一个文件描述符。你可以用dup()
创建一个新的文件描述符,或者用dup2()
把它复制到一个指定的文件描述符上。
/dev/null
是一个特殊的设备文件(在UNIX系统中,一切都是文件!),它就像一个黑洞,任何写入它的内容都会被吞掉。dup
是用来复制文件描述符的。如果你习惯了Windows,UNIX中的文件描述符就是一个特殊的整数,代表一个打开的文件,类似于Windows中的文件句柄。
这个程序是打开 /dev/null
进行写入(仅限写入),然后复制它的文件描述符,接着关闭这个打开的文件(因为在UNIX中,只要有文件描述符,就可以写入文件,不需要一直保持资源打开),最后将这个打开的文件分配给 sys.stdout
。
要记住,sys
是Python的一个模块,代表各种系统特定的资源,比如文件系统。所以在UNIX中,sys.stdout
代表 /dev/stdout
,也就是系统的 STDOUT
流。
总的来说,这段代码让Python误以为 /dev/null/
是 STDOUT
,所以每当你的程序通过 print
语句(在Python3中是一个函数)写入 STDOUT
时,实际上它是在写入 /dev/null
,你将永远看不到控制台上显示的文本。
我在下面写了一些额外的评论,应该能让你更清楚地理解redirect_stdout
函数内部发生了什么:
def redirect_stdout():
print "Redirecting stdout"
sys.stdout.flush() # <--- important when redirecting to files
# Duplicate stdout (file descriptor 1)
# to a different file descriptor number
newstdout = os.dup(1)
# /dev/null is used just to discard what is being printed
devnull = os.open('/dev/null', os.O_WRONLY)
# Duplicate the file descriptor for /dev/null
# and overwrite the value for stdout (file descriptor 1)
os.dup2(devnull, 1)
# Close devnull after duplication (no longer needed)
os.close(devnull)
# Use the original stdout to still be able
# to print to stdout within python
sys.stdout = os.fdopen(newstdout, 'w')
一个重要的事情是,当一个程序启动时,它会从操作系统那里获得三个不同的文件描述符:
- 标准输入(stdin):0
- 标准输出(stdout):1
- 标准错误(stderr):2
正如评论中所解释的,上面的代码利用了标准输出的文件描述符和文件描述符复制的功能,巧妙地让C语言代码使用一个不同的标准输出,同时在Python代码中保留对原始标准输出的引用,以便能够打印信息。