将标准输出写入文件时,Windows 上的 Python “句柄无效”

Posted

技术标签:

【中文标题】将标准输出写入文件时,Windows 上的 Python “句柄无效”【英文标题】:Python on Windows "Handle Invalid" when redirecting stdout writing to file 【发布时间】:2019-02-21 16:47:06 【问题描述】:

我正在尝试修复的脚本使用以下范例将标准输出重定向到文件。

import os
stdio_file = 'temp.out'
flag = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
stdio_fp = os.open(stdio_file, flag)
os.dup2(stdio_fp, 1)
print("hello")

在 Python 2 上,这是可行的。在 Python 3 上,您会收到 OSError

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print("hello")
OSError: [WinError 6] The handle is invalid
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
OSError: [WinError 6] The handle is invalid

我认为通过文件路由标准输出有更可取的方法,但我想知道为什么这个方法在 Python 3 中停止工作,是否有一种简单的方法来修复它?

【问题讨论】:

@eryksun 您的 cmets 是一个很好的答案。为什么不合二为一呢? 你是对的,设置PYTHONLEGACYWINDOWSSTDIO 可以解决问题。但是,我仍在努力解决脑海中的附加评论。我对很多操作系统的东西有点陌生。 【参考方案1】:

os.dup2(stdio_fp, 1) 之类的代码将在 Python 3.5 及更早版本中运行,或者在定义了环境变量 PYTHONLEGACYWINDOWSSTDIO 的 3.6+ 中运行。

问题在于 print 写入了仅用于控制台 I/O 的 sys.stdout 对象。具体来说,在 3.6+ 中,当 stdout 最初是一个控制台文件1 时,Python 3 的标准输出文件(即sys.stdout.buffer.raw)的原始层是一个io._WindowsConsoleIO 实例。此对象缓存标准输出文件描述符2 的初始句柄值。随后,dup2 关闭此句柄,同时将文件描述符与“temp.out”的重复句柄重新关联。此时缓存的句柄不再有效。 (实际上,它不应该缓存句柄,因为调用 _get_osfhandle 与控制台 I/O 的成本相比相对便宜。)但是,即使它有一个有效的“temp.out”句柄,sys.stdout.write无论如何都会失败,因为_WindowsConsoleIO 使用仅控制台功能WriteConsoleW 而不是通用WriteFile

您需要重新分配sys.stdout,而不是使用dup2 等低级操作绕过Python 的I/O 堆栈。我知道从 Unix 开发人员的角度来看这并不理想。我希望我们可以重新实现 Windows 控制台支持 Unicode 的方式,而无需引入这个仅限控制台的 _WindowsConsoleIO 类,它会破坏人们几十年来一直依赖的低级模式。


1. _WindowsConsoleIO 被添加以支持 Windows 控制台中的全部 Unicode(至少控制台可以支持它)。为此,它使用控制台的 UTF-16 宽字符 API(例如 ReadConsoleWWriteConsoleW)。以前 CPython 的控制台支持仅限于使用 Windows 代码页编码的文本,使用通用的基于字节的 I/O(例如 ReadFileWriteFile)。

2。 Windows 使用句柄来引用内核对象,例如文件对象。该系统在行为上与 POSIX 文件描述符 (FD) 不兼容。因此,C 运行时 (CRT) 具有“低 I/O”兼容性层,它将 POSIX 样式的 FD 与 Windows 文件句柄相关联,并且它还实现了 POSIX I/O 函数,例如 openwrite。 CRT 的_open_osfhandle 函数将本机文件句柄与FD 关联,_get_osfhandle 返回与FD 关联的句柄。有时 CPython 使用 CRT 低 I/O 层,有时它直接使用 Windows API。如果你问我,这真的有点乱。

【讨论】:

以上是关于将标准输出写入文件时,Windows 上的 Python “句柄无效”的主要内容,如果未能解决你的问题,请参考以下文章

如何将终端输出的信息重定向写入文件中呢?

以二进制模式写入标准输出的最简单方法是啥?

如何将wc的标准输出写入文件?

如何将 CSV 输出写入标准输出?

Linux基础命令

将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件