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

Nat*_*eed 45 python windows temporary-files

我正在编写一个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)
Run Code Online (Sandbox Code Playgroud)

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

Cor*_*bin 24

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

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)
Run Code Online (Sandbox Code Playgroud)

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

无论如何,是的,使用NamedTemporaryFileWindows上可能是不雅,在这里我的解决方案也可能是不雅的,但你已经决定支持Windows比优雅的代码更重要,所以你还不如继续前进,做一些可读性.

  • 使用`shutil.rmtree(temp_dir)`可以简化清理代码 (3认同)
  • 警告!**这根本不回答原始问题.**不仅这不比OP已经做的好,而且更糟糕的是将临时文件创建的脆弱方面留给用户-`make_a_file_in_a_dir(temp_dir)`.请查看我的答案,以便更好地处理此问题.(我不明白为什么四年前我第一次阅读这篇文章时没有写过这篇评论......). (3认同)
  • Downvote.虽然这个答案提出了原始问题的解决方案(在子进程中打开临时文件),但这不是原始问题中包含的答案更优雅的答案,因为这个答案使用较低级别的功能并忽略了一般的临时文件问题(例如安全性). (2认同)
  • 我刚刚尝试过这个,但我的版本不起作用。创建临时文件是:`cert_file = tempfile.NamedTemporaryFile(dir=temp_dir)` (2认同)

Pio*_*ost 22

理查德奥德柯克

(...)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)
Run Code Online (Sandbox Code Playgroud)

因为Python 2.x opener中的内置函数没有参数,所以open()我们必须结合较低级别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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这仅在您控制子流程中的代码时才有效.调用像`tar`这样的系统实用程序并不简单.仍然,整洁的发现! (3认同)
  • 如果您控制子流程的代码,这是最好的解决方案。如果不是这样,我没有比原始问题更优雅的解决方案了。 (2认同)

tsh*_*ang 12

您可以随时进入低级别,但我不确定它是否足够干净:

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)
Run Code Online (Sandbox Code Playgroud)


Nil*_*asu 9

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

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

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

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

编辑:事实证明,也可以从Windows中的多个进程打开和读取临时文件.见Piotr Dobrogost的回答.