gpt4 book ai didi

python - python 中的 dup、dup2、tmpfile 和 stdout

转载 作者:太空宇宙 更新时间:2023-11-03 12:34:39 25 4
gpt4 key购买 nike

这是来自 here 的后续问题.


我想去的地方

我希望能够暂时将 stdout 重定向到一个临时文件,而 python 仍然能够打印到 stdout。这将涉及以下步骤:

  1. 创建 stdout 的副本(new)
  2. 创建临时文件 (tmp)
  3. 将 stdout 重定向到 tmp
  4. 告诉 python 使用 new 作为标准输出
  5. tmp 重定向到“真正的”标准输出
  6. 告诉 python 再次使用“真正的”标准输出
  7. 读取并关闭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()

我想在这里休息一下,总结一下。
到这里为止的控制台输出应该是:

0.1
0.2
print

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())

发生错误,输出如下所示:

1.1
1.2
/bin/sh: line 0: echo: write error: Bad file descriptor
print

sil 读取:['0.3\n', '']

换句话说:第二个 Func("1.3", True) 无法写入临时文件。

问题

  1. 首先,我想知道为什么我的脚本没有像我希望的那样工作。意思是,为什么只能在脚本的前半部分写入临时文件?
  2. 对于dupdup2 的用法,我还是有些疑惑。虽然我认为我理解将 stdout 重定向到临时文件是如何工作的,但我现在完全知道为什么 os.dup2(new, 1) 正在做它正在做的事情。也许答案可以详细说明我脚本中的所有 dupdup2 在做什么^^

最佳答案

您获得“错误文件描述符”的原因是垃圾收集器为您关闭了标准输出 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

现在,当这两个文件中的第二个被执行时,第一个文件对象的引用计数降为零,垃圾收集器将其销毁。文件对象在析构时关闭它们关联的 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)

我实际上碰巧有一个上下文管理器,我认为它确实(或几乎至少,在我的情况下,我碰巧需要一个命名的临时文件)你正在寻找的东西。您可以看到它重用了原始的 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()))

关于python - python 中的 dup、dup2、tmpfile 和 stdout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8817993/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com