首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python中的dup、dup2、tmpfile和stdout

python中的dup、dup2、tmpfile和stdout
EN

Stack Overflow用户
提问于 2012-01-11 10:50:21
回答 1查看 11.2K关注 0票数 6

这是here的后续问题。

我想去的地方

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

创建stdout (new)

  • Create的副本,将临时文件(tmp)

  • Redirect stdout转换为tmp

  • Tell python,将new作为stdout

  • Redirect tmp使用到“真实”的stdout

  • 中,告诉python再次使用“真正的”stdout,

  • 读取和关闭tmp

实现

我试图以以下方式实现上述内容:

代码语言:javascript
复制
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()

我想在这里休息一下,总结一下。

在这里之前,要控制台的输出应该是:

代码语言:javascript
复制
0.1
0.2
print

sil应该是这样的:['0.3\n']。所以在这之前,一切都很有魅力。但是,如果我再次重做上面的脚本,如下所示:

代码语言:javascript
复制
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())

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

代码语言:javascript
复制
1.1
1.2
/bin/sh: line 0: echo: write error: Bad file descriptor
print

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

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

问题

首先,我想知道为什么我的脚本不像我想要的那样工作。也就是说,为什么只有在脚本的前半部分才可能写入临时文件?

  • --我仍然对dupdup2的用法感到有点困惑。虽然我想我理解将stdout重定向到临时文件是如何工作的,但我现在完全明白了为什么os.dup2(new, 1)要做它正在做的事情。也许答案可以详细说明我的脚本中的所有dupdup2在做什么^

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-01-11 19:29:11

您获得“坏文件描述符”的原因是垃圾收集器为您关闭了stdout。考虑这两行:

代码语言:javascript
复制
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时,它会触发“坏文件描述符”错误。

代码语言:javascript
复制
import os
whatever = os.fdopen(1, 'w', 0)
os.fstat(1)
del whatever
os.fstat(1)

实际上,我碰巧有一个上下文管理器,我认为它恰好(或者至少在我的例子中,我需要一个命名的tempfile)就是您所要寻找的。您可以看到,它重用原始的sys.stdout对象以避免关闭问题。

代码语言:javascript
复制
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()))
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8817993

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档