【问题标题】:How to create a temporary file that can be read by a subprocess?如何创建一个子进程可以读取的临时文件?
【发布时间】:2013-02-16 15:00:23
【问题描述】:

我正在编写一个 Python 脚本,该脚本需要将一些数据写入临时文件,然后创建一个运行 C++ 程序的子进程来读取临时文件。我正在尝试为此使用NamedTemporaryFile,但根据文档,

在命名的临时文件仍处于打开状态时,该名称是否可用于第二次打开文件,因平台而异(在 Unix 上可以这样使用;在 Windows NT 或更高版本上不能)。

确实,在 Windows 上,如果我在写入后刷新临时文件,但在我希望它消失之前不要关闭它,子进程将无法打开它进行读取。

我正在通过使用delete=False 创建文件,在生成子进程之前关闭它,然后在完成后手动删除它来解决这个问题:

fileTemp = tempfile.NamedTemporaryFile(delete = False)
try:
    fileTemp.write(someStuff)
    fileTemp.close()
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(fileTemp.name)

这似乎不优雅。有一个更好的方法吗?也许一种方法可以打开临时文件的权限以便子进程可以访问它?

【问题讨论】:

标签: python windows temporary-files


【解决方案1】:

因为似乎没有其他人有兴趣公开这些信息......

tempfile 确实公开了一个函数mkdtemp(),它可以解决这个问题:

try:
    temp_dir = mkdtemp()
    temp_file = make_a_file_in_a_dir(temp_dir)
    do_your_subprocess_stuff(temp_file)
    remove_your_temp_file(temp_file)
finally:
    os.rmdir(temp_dir)

我将中间函数的实现留给读者,因为人们可能希望使用mkstemp() 来加强临时文件本身的安全性,或者在删除之前就地覆盖文件。我并不特别知道通过仔细阅读tempfile 的源代码,人们可能会遇到哪些安全限制不容易计划好。

无论如何,是的,在 Windows 上使用 NamedTemporaryFile 可能不优雅,我在这里的解决方案也可能不优雅,但是您已经确定 Windows 支持比优雅的代码更重要,所以您不妨继续做一些可读的事情。

【讨论】:

  • 清理代码可以简化为shutil.rmtree(temp_dir)
  • 我可能弄错了,但这看起来是最好的答案。
  • 小心! 这根本不能回答原始问题。 这不仅没有比 OP 已经在做的更好,而且更糟糕的是,将创建临时文件的脆弱方面留给用户 –make_a_file_in_a_dir(temp_dir)。请参阅我的答案以获得更好的处理方法。 (我不明白为什么四年前我第一次读到这篇文章时没有写这篇评论……)。
  • 投反对票。尽管此答案提出了原始问题的解决方案(在子进程中打开临时文件),但这并不是比原始问题中包含的答案更优雅的答案,因为此答案使用较低级别的功能并忽略了一般的临时文件问题(例如安全性)。
  • local variable 'temp_dir' referenced before assignment。我猜实际的创建必须走出“尝试”,这使得它很难调试?
【解决方案2】:

According 致 Richard Oudkerk

(...) 尝试重新打开 NamedTemporaryFile 失败的唯一原因是 Windows是因为我们重新打开时需要使用O_TEMPORARY

他举了一个例子说明如何在 Python 3.3+ 中做到这一点

import os, tempfile

DATA = b"hello bob"

def temp_opener(name, flag, mode=0o777):
    return os.open(name, flag | os.O_TEMPORARY, mode)

with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()
    with open(f.name, "rb", opener=temp_opener) as f:
        assert f.read() == DATA

assert not os.path.exists(f.name)

由于 Python 2.x 内置的open() 中没有opener 参数,所以我们必须结合较低级别的os.open()os.fdopen() 函数来达到相同的效果:

import subprocess
import tempfile

DATA = b"hello bob"

with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()

    subprocess_code = \
    """import os
       f = os.fdopen(os.open(r'{FILENAME}', os.O_RDWR | os.O_BINARY | os.O_TEMPORARY), 'rb')
       assert f.read() == b'{DATA}'
    """.replace('\n', ';').format(FILENAME=f.name, DATA=DATA)

    subprocess.check_output(['python', '-c', subprocess_code]) == DATA

【讨论】:

  • 有趣...我在 Python 2.x 中,但我必须进一步研究。
  • @NathanReed 在 Python 2.x 中也是可能的 - 请参阅我的更新。
  • 请注意,这仅在您控制子流程中的代码时才有效。调用像tar 这样的系统实用程序不会很简单。不过,很好的发现!
  • @yucer 仔细看;您显示的页面中每组常量的标签都在该组下方而不是上方。
  • 如果控制子流程的代码,这是最好的解决方案。如果没有,我没有看到比原始问题更优雅的解决方案。
【解决方案3】:

你总是可以去低级,但不确定它是否对你来说足够干净:

fd, filename = tempfile.mkstemp()
try:
    os.write(fd, someStuff)
    os.close(fd)
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(filename)

【讨论】:

    【解决方案4】:

    至少如果您使用现有的 Python 库打开一个临时文件,则在 Windows 的情况下无法从多个进程访问它。根据MSDN,您可以指定第三个参数(dwSharedMode)共享模式标志FILE_SHARE_READCreateFile() 函数:

    启用对文件或设备的后续打开操作以请求读取 使用权。否则,其他进程无法打开文件或设备,如果 他们请求读取权限。如果未指定此标志,但文件 或设备已打开进行读取访问,该功能失败。

    因此,您可以编写一个特定于 Windows 的 C 例程来创建一个自定义的临时文件打开器函数,从 Python 调用它,然后您可以让您的子进程访问该文件而不会出现任何错误。但我认为你应该坚持你现有的方法,因为它是最便携的版本,可以在任何系统上运行,因此是最优雅的实现。

    • 关于 Linux 和 windows 文件锁定的讨论可以找到here

    编辑:事实证明,也可以从 Windows 中的多个进程打开和读取临时文件。请参阅 Piotr Dobrogost 的 answer

    【讨论】:

    • 谢谢。我希望 Python API 会公开这些模式标志,但我想我可以明白为什么它们不公开。哦,好吧。
    • 至少如果你使用现有的 Python 库打开一个临时文件,在 Windows 的情况下是不可能从多个进程访问它的。 这不是真的。见bugs.python.org/issue14243#msg164504 和我的answer
    【解决方案5】:

    with 语句中使用mkstemp() 代替os.fdopen() 可以避免调用close()

    fd, path = tempfile.mkstemp()
    try:
        with os.fdopen(fd, 'wb') as fileTemp:
            fileTemp.write(someStuff)
        # ...run the subprocess and wait for it to complete...
    finally:
        os.remove(path)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-05
      • 2018-10-19
      • 1970-01-01
      • 2017-09-13
      • 1970-01-01
      • 1970-01-01
      • 2013-01-25
      • 1970-01-01
      相关资源
      最近更新 更多