将标准输出重定向到文件

时间:2018-09-17 17:43:56

标签: python windows python-3.x operating-system

我要修复的脚本使用以下范例将stdout重定向到文件。

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

我假设有更好的方法来通过文件路由stdout,但是我想知道为什么该方法在Python 3中停止工作,并且是否有一种简单的方法来解决它?

1 个答案:

答案 0 :(得分:2)

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

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

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


1。添加了_WindowsConsoleIO以在Windows控制台中支持所有范围的Unicode(至少在控制台可以支持的范围内)。为此,它使用控制台的UTF-16宽字符API(例如_WindowsConsoleIOReadConsoleW)。以前,CPython的控制台支持仅限于使用基于字节的通用I / O(例如WriteConsoleWReadFile)和Windows代码页编码的文本。

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