从python重定向stdout进行C调用

Wol*_*tan 9 python stdout io-redirection dup2 dup

这是从一个跟进的问题在这里专门涉及了答案.


从python模块我调用一个Hello World可执行文件,只是打印Hello World到stdout.我有兴趣将该输出重定向到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')
Run Code Online (Sandbox Code Playgroud)

此外,我想恢复重定向之前的stdout.

问题

  1. 上面的函数究竟发生了什么?
    • 是什么dupdup2做什么?
    • 什么是/dev/null
    • 第8行在做什么?(sys.stdout = os.fdopen(newstdout, 'w'))
  2. 如何将stdout存储在StringIO对象中?
  3. 如何在调用Hello World程序后恢复标准输出?

我很确定,一旦我得到了问题1的答案,问题2和3的答案就很容易了.无论如何我决定发布它们可能会将问题1的答案推到我想去的方向.

jco*_*ado 11

我在下面写了一些额外的注释,这些注释应该更清楚它在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')
Run Code Online (Sandbox Code Playgroud)

需要注意的一件重要事情是,一个进程在启动时从操作系统中获取三个不同的文件描述符:

  • stdin:0
  • 标准:1
  • stderr:2

正如评论中所解释的那样,上面的代码利用stdout的文件描述符和文件描述符复制函数来使C代码使用不同的stdout,同时仍然保持对python代码中原始stdout的引用能够打印.


sni*_*im2 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所以现在每次你的程序写入的时间STDOUT用,比方说,print声明(在Python3功能),那么它真的会写/dev/null,你将永远看不到你的控制台上生成的文本.