【问题标题】:Howto: workaround of close_fds=True and redirect stdout/stderr on windows如何:close_fds=True 的解决方法并在 Windows 上重定向 stdout/stderr
【发布时间】:2018-02-07 18:50:05
【问题描述】:

我遇到了一个问题:使用 Python 2.7,无法使用创建子进程

subprocess.Popen([.......], close_fds=True, stdout=subprocess.PIPE, ...)

由于限制,在 Windows 上。在我的情况下需要使用close_fds,因为我不希望子进程从已经打开的文件文件描述符继承。这是在库中调用的,这意味着我无法控制已打开的文件描述符(N 标志)。

这是一个known bug,它被固定在Python 3.4+

我的问题是:如何在不获取的情况下使用子流程

如果重定向 stdin/stdout/stderr,Windows 平台不支持 close_fds

下面回答

【问题讨论】:

    标签: python windows subprocess


    【解决方案1】:

    此问题在 Python 3.7+ 上已默认修复

    这绝对是一个棘手的技巧:答案是在使用 subprocess 模块之前遍历已经打开的文件描述符。

    def _hack_windows_subprocess():
        """HACK: python 2.7 file descriptors.
        This magic hack fixes https://bugs.python.org/issue19575
        by adding HANDLE_FLAG_INHERIT to all already opened file descriptors.
        """
        # See https://github.com/secdev/scapy/issues/1136
        import stat
        from ctypes import windll, wintypes
        from msvcrt import get_osfhandle
    
        HANDLE_FLAG_INHERIT = 0x00000001
    
        for fd in range(100):
            try:
                s = os.fstat(fd)
            except:
                continue
            if stat.S_ISREG(s.st_mode):
                handle = wintypes.HANDLE(get_osfhandle(fd))
                mask   = wintypes.DWORD(HANDLE_FLAG_INHERIT)
                flags  = wintypes.DWORD(0)
                windll.kernel32.SetHandleInformation(handle, mask, flags)
    

    这是一个没有它会崩溃的示例:

    import os, subprocess
    f = open("a.txt", "w")
    subprocess.Popen(["cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    f.close()
    os.remove(f.name)
    

    Traceback(最近一次调用最后一次):

    文件“stdin”,第 1 行,在模块中

    WindowsError: [Error 32] Le processus ne peut pas accÚder au fichier car ce fichier est utilisÚ par un autre processus: 'a.txt'

    现在修复:

    import os, subprocess
    f = open("a.txt", "w")
    _hack_windows_subprocess()
    subprocess.Popen(["cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    f.close()
    os.remove(f.name)
    

    工作。

    希望对我有所帮助

    【讨论】:

    • 在 Windows 上的 Python 2.7 中,您可以将文件显式打开为 [N]on-inheritable,例如f = open("a.txt", "wN")。在 Python 3 中,这是默认设置。
    • 这个是对的,上面的例子只在程序无法控制之前打开的文件时使用(例如API)
    • 此外,在 3.7(仍处于测试阶段)中,子进程使用 PROC_THREAD_ATTRIBUTE_HANDLE_LIST 来限制哪些可继承句柄实际上被继承来解决此问题。
    猜你喜欢
    • 2015-09-02
    • 1970-01-01
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多