如何创建一个可被子进程读取的临时文件?
我正在写一个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 个回答
因为似乎没有其他人愿意把这些信息公开出来...
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 的支持比优雅的代码更重要,所以不妨直接写一些容易理解的代码。
根据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
在Windows系统中,如果你用现有的Python库打开一个临时文件,多个进程是无法同时访问这个文件的。根据MSDN的说法,你可以在调用CreateFile()
函数时,指定一个第三个参数(dwSharedMode
),这个参数是共享模式标志FILE_SHARE_READ
,它的作用是:
允许后续的打开操作请求对文件或设备的读取权限。否则,如果其他进程请求读取权限,它们就无法打开这个文件或设备。如果没有指定这个标志,但文件或设备已经被打开用于读取,函数就会失败。
所以,你可以写一个Windows特定的C语言程序,创建一个自定义的临时文件打开函数,然后从Python调用它,这样你的子进程就可以访问这个文件而不会出错。不过,我觉得你还是应该坚持使用你现在的方法,因为它是最通用的,可以在任何系统上工作,因此是最优雅的实现方式。
- 关于Linux和Windows文件锁定的讨论可以在这里找到。
编辑:结果发现,在Windows中也可以让多个进程打开和读取临时文件。可以查看Piotr Dobrogost的回答。