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

68 投票
6 回答
57610 浏览
提问于 2025-04-17 17:39

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

这看起来不太优雅。有没有更好的方法?也许可以打开临时文件的权限,让子进程能够访问它?

6 个回答

27

因为似乎没有其他人愿意把这些信息公开出来...

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 的支持比优雅的代码更重要,所以不妨直接写一些容易理解的代码。

27

根据Richard 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
11

在Windows系统中,如果你用现有的Python库打开一个临时文件,多个进程是无法同时访问这个文件的。根据MSDN的说法,你可以在调用CreateFile()函数时,指定一个第三个参数(dwSharedMode),这个参数是共享模式标志FILE_SHARE_READ,它的作用是:

允许后续的打开操作请求对文件或设备的读取权限。否则,如果其他进程请求读取权限,它们就无法打开这个文件或设备。如果没有指定这个标志,但文件或设备已经被打开用于读取,函数就会失败。

所以,你可以写一个Windows特定的C语言程序,创建一个自定义的临时文件打开函数,然后从Python调用它,这样你的子进程就可以访问这个文件而不会出错。不过,我觉得你还是应该坚持使用你现在的方法,因为它是最通用的,可以在任何系统上工作,因此是最优雅的实现方式。

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

编辑:结果发现,在Windows中也可以让多个进程打开和读取临时文件。可以查看Piotr Dobrogost的回答

撰写回答