繁体   English   中英

如何创建一个子进程可以读取的临时文件?

[英]How to create a temporary file that can be read by a subprocess?

我正在编写一个 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)

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

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

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 支持比优雅的代码更重要,所以您不妨继续做一些可读的事情。

根据理查德 Oudkerk

(...) 在 Windows 上尝试重新打开NamedTemporaryFile失败的唯一原因是,当我们重新打开时,我们需要使用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

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

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)

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

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

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

  • 关于 Linux 和 windows 文件锁定的讨论可以在这里找到。

编辑:原来也可以从 Windows 中的多个进程打开和读取临时文件。 请参阅 Piotr Dobrogost 的回答

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)

我知道这是一篇很老的帖子,但我认为它在今天是相关的,因为 API 正在发生变化,并且 mktemp 和 mkstemp 等函数正在被 TemporaryFile() 和 TemporaryDirectory() 等函数取代。 我只是想在以下示例中演示如何确保临时目录在下游仍然可用:

而不是编码:

tmpdirname = tempfile.TemporaryDirectory()

并在整个代码中使用 tmpdirname ,您应该尝试在 with 语句块中使用您的代码,以确保它可用于您的代码调用......像这样:

with tempfile.TemporaryDirectory() as tmpdirname:
    [do dependent code nested so it's part of the with statement]

如果您在 with 之外引用它,那么它很可能不再可见。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM