将Python的标准输出重定向到C调用

11 投票
3 回答
5658 浏览
提问于 2025-04-17 09:56

这是一个跟这里的问题相关的后续提问,特别是关于它的回答


我在一个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')

另外,我还想在重定向后恢复标准输出,回到之前的状态。

问题

  1. 上面的函数到底在做什么?
    • dupdup2在干什么?
    • /dev/null是什么?
    • 第8行在做什么?(sys.stdout = os.fdopen(newstdout, 'w')
  2. 我怎么能把标准输出存储到一个StringIO对象中?
  3. 在调用我的Hello World程序之后,我怎么能恢复标准输出?

我很确定,一旦我弄清楚了第一个问题的答案,第二和第三个问题的答案就会变得简单。我决定把这些问题发出来,或许能把第一个问题的答案引向我想要的方向。

3 个回答

-1

可以查看手册页面,了解这些Python函数背后的C语言运行时函数。

简单来说,这些函数的作用是复制一个文件描述符。你可以用dup()创建一个新的文件描述符,或者用dup2()把它复制到一个指定的文件描述符上。

7

/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,你将永远看不到控制台上显示的文本。

14

我在下面写了一些额外的评论,应该能让你更清楚地理解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代码中保留对原始标准输出的引用,以便能够打印信息。

撰写回答