如何创build一个可以被subprocess读取的临时文件?

我正在编写一个Python脚本,需要将一些数据写入临时文件,然后创build一个运行C ++程序的subprocess来读取临时文件。 我试图使用NamedTemporaryFile这个,但根据文档,

当命名的临时文件仍然打开时,名称是否可用于第二次打开该文件,在不同的平台上是不同的(它可以在Unix上使用,不能在Windows NT或更高版本上使用)。

事实上,在Windows上,如果在写入后刷新临时文件,但是不要closures它,直到我想让它消失,则subprocess无法打开它进行读取。

我正在通过创build文件与delete=False ,在产卵subprocess之前closures它,然后手动删除它,一旦我完成:

 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) 

这似乎不雅。 有一个更好的方法吗? 也许一种方法来打开临时文件的权限,以便subprocess可以得到它?

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

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

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

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

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

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

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的源tempfile ,这些限制是不容易计划的。

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

根据理查德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 

你总是可以进入低级别,但不知道它是否足够干净。

 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)