Python os.dup2 重定向在 windows python 控制台上启用输出缓冲
Python os.dup2 redirect enables output buffering on windows python consoles
我正在使用基于 os.dup2 的策略(类似于本网站上的示例)将 C/fortran 级输出重定向到一个临时文件以进行捕获。
我注意到的唯一问题是,如果您从 windows(python.exe 或 ipython)中的交互式 shell 使用此代码,它有在控制台中启用输出缓冲的奇怪副作用。
捕获前 sys.stdout
是 returns True
为 istty()
的某种文件对象。输入 print('hi')
会导致直接输出 hi。
捕获后 sys.stdout
指向完全相同的文件对象,但 print('hi')
不再显示任何内容,直到调用 sys.stdout.flush()
。
下面是一个最小的示例脚本"test.py"
import os, sys, tempfile
class Capture(object):
def __init__(self):
super(Capture, self).__init__()
self._org = None # Original stdout stream
self._dup = None # Original system stdout descriptor
self._file = None # Temporary file to write stdout to
def start(self):
self._org = sys.stdout
sys.stdout = sys.__stdout__
fdout = sys.stdout.fileno()
self._file = tempfile.TemporaryFile()
self._dup = None
if fdout >= 0:
self._dup = os.dup(fdout)
os.dup2(self._file.fileno(), fdout)
def stop(self):
sys.stdout.flush()
if self._dup is not None:
os.dup2(self._dup, sys.stdout.fileno())
os.close(self._dup)
sys.stdout = self._org
self._file.seek(0)
out = self._file.readlines()
self._file.close()
return out
def run():
c = Capture()
c.start()
os.system('echo 10')
print('20')
x = c.stop()
print(x)
if __name__ == '__main__':
run()
打开命令提示符,运行脚本运行正常。这会产生预期的输出:
python.exe test.py
运行 它来自 python shell 不是:
python.exe
>>> import test.py
>>> test.run()
>>> print('hello?')
在刷新标准输出之前不显示输出:
>>> import sys
>>> sys.stdout.flush()
有人知道发生了什么事吗?
快速信息:
- 问题出现在 Windows 上,而不是 出现在 linux 上(所以可能不会出现在 mac 上)。
- Python 2.7.6 和 Python 2.7.9
中的行为相同
- 脚本应该捕获 C/fortran 输出,而不仅仅是 python 输出
- 它在 windows 上运行没有错误,但之后 print() 不再刷新
我 可以确认 Python 2 在 Linux 中的相关问题,但 Python 3
没有
基本问题是
>>> sys.stdout is sys.__stdout__
True
因此您一直使用原始 sys.stdout
对象。当您执行 第一次输出 时,在 Python 2 中它会为底层文件执行一次 isatty()
系统调用,并存储结果。
您应该打开一个全新的文件并用它替换 sys.stdout
。
因此 Capture
class 的正确写法是
import sys
import tempfile
import time
import os
class Capture(object):
def __init__(self):
super(Capture, self).__init__()
def start(self):
self._old_stdout = sys.stdout
self._stdout_fd = self._old_stdout.fileno()
self._saved_stdout_fd = os.dup(self._stdout_fd)
self._file = sys.stdout = tempfile.TemporaryFile(mode='w+t')
os.dup2(self._file.fileno(), self._stdout_fd)
def stop(self):
os.dup2(self._saved_stdout_fd, self._stdout_fd)
os.close(self._saved_stdout_fd)
sys.stdout = self._old_stdout
self._file.seek(0)
out = self._file.readlines()
self._file.close()
return out
def run():
c = Capture()
c.start()
os.system('echo 10')
x = c.stop()
print(x)
time.sleep(1)
print("finished")
run()
使用此程序,在 Python 2 和 Python 3 中,输出将是:
['10\n']
finished
第一行立即出现在终端上,第二行在延迟一秒后出现。
然而,对于从 sys
导入 stdout
的代码,这将失败。幸运的是,没有多少代码可以做到这一点。
我正在使用基于 os.dup2 的策略(类似于本网站上的示例)将 C/fortran 级输出重定向到一个临时文件以进行捕获。
我注意到的唯一问题是,如果您从 windows(python.exe 或 ipython)中的交互式 shell 使用此代码,它有在控制台中启用输出缓冲的奇怪副作用。
捕获前 sys.stdout
是 returns True
为 istty()
的某种文件对象。输入 print('hi')
会导致直接输出 hi。
捕获后 sys.stdout
指向完全相同的文件对象,但 print('hi')
不再显示任何内容,直到调用 sys.stdout.flush()
。
下面是一个最小的示例脚本"test.py"
import os, sys, tempfile
class Capture(object):
def __init__(self):
super(Capture, self).__init__()
self._org = None # Original stdout stream
self._dup = None # Original system stdout descriptor
self._file = None # Temporary file to write stdout to
def start(self):
self._org = sys.stdout
sys.stdout = sys.__stdout__
fdout = sys.stdout.fileno()
self._file = tempfile.TemporaryFile()
self._dup = None
if fdout >= 0:
self._dup = os.dup(fdout)
os.dup2(self._file.fileno(), fdout)
def stop(self):
sys.stdout.flush()
if self._dup is not None:
os.dup2(self._dup, sys.stdout.fileno())
os.close(self._dup)
sys.stdout = self._org
self._file.seek(0)
out = self._file.readlines()
self._file.close()
return out
def run():
c = Capture()
c.start()
os.system('echo 10')
print('20')
x = c.stop()
print(x)
if __name__ == '__main__':
run()
打开命令提示符,运行脚本运行正常。这会产生预期的输出:
python.exe test.py
运行 它来自 python shell 不是:
python.exe
>>> import test.py
>>> test.run()
>>> print('hello?')
在刷新标准输出之前不显示输出:
>>> import sys
>>> sys.stdout.flush()
有人知道发生了什么事吗?
快速信息:
- 问题出现在 Windows 上,而不是 出现在 linux 上(所以可能不会出现在 mac 上)。
- Python 2.7.6 和 Python 2.7.9 中的行为相同
- 脚本应该捕获 C/fortran 输出,而不仅仅是 python 输出
- 它在 windows 上运行没有错误,但之后 print() 不再刷新
我 可以确认 Python 2 在 Linux 中的相关问题,但 Python 3
没有基本问题是
>>> sys.stdout is sys.__stdout__
True
因此您一直使用原始 sys.stdout
对象。当您执行 第一次输出 时,在 Python 2 中它会为底层文件执行一次 isatty()
系统调用,并存储结果。
您应该打开一个全新的文件并用它替换 sys.stdout
。
因此 Capture
class 的正确写法是
import sys
import tempfile
import time
import os
class Capture(object):
def __init__(self):
super(Capture, self).__init__()
def start(self):
self._old_stdout = sys.stdout
self._stdout_fd = self._old_stdout.fileno()
self._saved_stdout_fd = os.dup(self._stdout_fd)
self._file = sys.stdout = tempfile.TemporaryFile(mode='w+t')
os.dup2(self._file.fileno(), self._stdout_fd)
def stop(self):
os.dup2(self._saved_stdout_fd, self._stdout_fd)
os.close(self._saved_stdout_fd)
sys.stdout = self._old_stdout
self._file.seek(0)
out = self._file.readlines()
self._file.close()
return out
def run():
c = Capture()
c.start()
os.system('echo 10')
x = c.stop()
print(x)
time.sleep(1)
print("finished")
run()
使用此程序,在 Python 2 和 Python 3 中,输出将是:
['10\n']
finished
第一行立即出现在终端上,第二行在延迟一秒后出现。
然而,对于从 sys
导入 stdout
的代码,这将失败。幸运的是,没有多少代码可以做到这一点。