Wol*_*tan 3 python stdout io-redirection dup2 dup
这是从一个跟进的问题在这里.
我希望能够暂时将stdout重定向到临时文件,而python仍然可以打印到stdout.这将涉及以下步骤:
new)的副本tmp)tmpnewstdouttmp到"真正的"标准输出tmp我尝试通过以下方式实现上述方法:
import os
import subprocess
import sys
#A function that calls an external process to print to stdout as well as
#a python print to pythons stdout.
def Func(s, p = False):
subprocess.call('echo "{0}"'.format(s), shell = True)
if p:
print "print"
sil = list() # <-- Some list to store the content of the temp files
print "0.1" # Some testing of the
Func("0.2") # functionality
new = os.dup(1) # Create a copy of stdout (new)
tmp = os.tmpfile() # Create a temp file (tmp)
os.dup2(tmp.fileno(), 1) # Redirect stdout into tmp
sys.stdout = os.fdopen(new, 'w', 0) # Tell python to use new as stdout
Func("0.3", True) # <--- This should print "0.3" to the temp file and "print" to stdout
os.dup2(new, 1) # Redirect tmp into "real" stdout
sys.stdout = os.fdopen(1, 'w', 0) # Tell python to use "real" stdout again
# Read and close tmp
tmp.flush()
tmp.seek(0, os.SEEK_SET)
sil.append(tmp.read())
tmp.close()
Run Code Online (Sandbox Code Playgroud)
我想在这里稍作休息来总结一下.
到此为止的控制台输出应为:
0.1
0.2
print
Run Code Online (Sandbox Code Playgroud)
虽然sil应该是这样的:['0.3\n'].所以一切都像一个魅力,直到这里.但是,如果我再次重做上面的脚本:
print "1.1" # Some testing of the
Func("1.2") # functionality
new = os.dup(1) # Create a copy of stdout (new)
tmp = os.tmpfile() # Create a temp file (tmp)
os.dup2(tmp.fileno(), 1) # Redirect stdout into tmp
sys.stdout = os.fdopen(new, 'w', 0) # Tell python to use new as stdout
# This should print "0.3" to the temp file and "print" to stdout and is the crucial point!
Func("1.3", True)
os.dup2(new, 1) # Redirect tmp into "real" stdout
sys.stdout = os.fdopen(1, 'w', 0) # Tell python to use "real" stdout again
# Read and close tmp
tmp.flush()
tmp.seek(0, os.SEEK_SET)
sil.append(tmp.read())
Run Code Online (Sandbox Code Playgroud)
发生错误,输出如下所示:
1.1
1.2
/bin/sh: line 0: echo: write error: Bad file descriptor
print
Run Code Online (Sandbox Code Playgroud)
而sil读:['0.3\n', ''].
换句话说:第二个Func("1.3", True)是无法写入临时文件.
dup和我仍然有点困惑dup2.虽然我认为我理解如何将stdout重定向到临时文件中,但我现在完全知道为什么os.dup2(new, 1)要做它正在做的事情.也许答案可以详细说明我的剧本中的所有dup和dup2s正在做什么^^And*_*org 11
你得到"错误的文件描述符"的原因是垃圾收集器为你关闭了stdout FD.考虑以下两行:
sys.stdout = os.fdopen(1, 'w', 0) # from first part of your script
...
sys.stdout = os.fdopen(new, 'w', 0) # from second part of your script
Run Code Online (Sandbox Code Playgroud)
现在当执行这两个中的第二个时,第一个文件对象的引用计数降为零,垃圾收集器将其销毁.文件对象在被破坏时关闭其关联的fd,并且fd恰好是1 = stdout.所以你需要非常小心你如何销毁用它创建的对象os.fdopen.
这是一个显示问题的小例子.os.fstat仅用作示例函数,当您将其关闭为fd时触发"错误文件描述符"错误.
import os
whatever = os.fdopen(1, 'w', 0)
os.fstat(1)
del whatever
os.fstat(1)
Run Code Online (Sandbox Code Playgroud)
我实际上碰巧有一个上下文管理器,我认为完全(或几乎至少,在我的情况下,我发生需要一个命名的临时文件)你正在寻找什么.您可以看到它重用原始的sys.stdout对象以避免出现问题.
import sys
import tempfile
import os
class captured_stdout:
def __init__(self):
self.prevfd = None
self.prev = None
def __enter__(self):
F = tempfile.NamedTemporaryFile()
self.prevfd = os.dup(sys.stdout.fileno())
os.dup2(F.fileno(), sys.stdout.fileno())
self.prev = sys.stdout
sys.stdout = os.fdopen(self.prevfd, "w")
return F
def __exit__(self, exc_type, exc_value, traceback):
os.dup2(self.prevfd, self.prev.fileno())
sys.stdout = self.prev
##
## Example usage
##
## here is a hack to print directly to stdout
import ctypes
libc=ctypes.LibraryLoader(ctypes.CDLL).LoadLibrary("libc.so.6")
def directfdprint(s):
libc.write(1, s, len(s))
print("I'm printing from python before capture")
directfdprint("I'm printing from libc before captrue\n")
with captured_stdout() as E:
print("I'm printing from python in capture")
directfdprint("I'm printing from libc in capture\n")
print("I'm printing from python after capture")
directfdprint("I'm printing from libc after captrue\n")
print("Capture contains: " + repr(file(E.name).read()))
Run Code Online (Sandbox Code Playgroud)