使用NamedTemporaryFile上下文管理器时Linux文件未关闭报错问题
问题描述
我今天遇到一个问题,简化后的代码在Mac上能正常运行,但在Linux上报错:
import tempfile import subprocess import os with tempfile.NamedTemporaryFile() as tmp_file: with open(tmp_file.name, 'w') as writer: print("#!/bin/sh", file=writer) print("echo hi", file=writer) os.chmod(tmp_file.name, 0o511) subprocess.run([tmp_file.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
在Linux机器上收到错误:[Errno 26] Text file busy: '/tmp/tmp1y99vl6k',这说明调用subprocess时,writer的文件句柄实际并未关闭。
这是Python的预期行为吗?有没有办法在NamedTemporaryFile上下文内强制关闭文件,还是必须改用TemporaryDirectory?
我查到的相关说明:
当命名临时文件仍处于打开状态时,是否可以再次使用其名称打开该文件因平台而异(Unix系统可以;Windows系统不行)
解决方案
这是预期行为,根源在于不同Unix-like系统对"文件被打开时执行/重新打开"的处理逻辑不同:
- macOS允许在文件被其他句柄打开的情况下执行该文件
- Linux则会触发
Text file busy错误,因为NamedTemporaryFile默认会保持文件打开状态(上下文管理器持有一个句柄),即使你自己的writer已经关闭。
解决方法1:在NamedTemporaryFile中关闭句柄后重新打开
可以手动关闭tmp_file的句柄,操作完成后再重新打开它(确保上下文结束时能正常清理临时文件):
import tempfile import subprocess import os with tempfile.NamedTemporaryFile() as tmp_file: # 先关闭临时文件的句柄 tmp_file.close() # 写入脚本内容 with open(tmp_file.name, 'w') as writer: print("#!/bin/sh", file=writer) print("echo hi", file=writer) os.chmod(tmp_file.name, 0o511) # 执行脚本 subprocess.run([tmp_file.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 重新打开文件,确保上下文管理器能正常删除它 tmp_file = open(tmp_file.name, 'r')
解决方法2:使用delete=False参数,手动清理文件
如果不需要上下文自动删除文件,可以指定delete=False,操作完成后手动删除临时文件:
import tempfile import subprocess import os tmp_file = tempfile.NamedTemporaryFile(delete=False) tmp_file.close() try: with open(tmp_file.name, 'w') as writer: print("#!/bin/sh", file=writer) print("echo hi", file=writer) os.chmod(tmp_file.name, 0o511) subprocess.run([tmp_file.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) finally: os.unlink(tmp_file.name)
解决方法3:改用TemporaryDirectory
如果上面的方法都不符合需求,创建临时目录再在里面生成脚本文件也是一个稳妥的方案:
import tempfile import subprocess import os with tempfile.TemporaryDirectory() as tmp_dir: script_path = os.path.join(tmp_dir, "script.sh") with open(script_path, 'w') as writer: print("#!/bin/sh", file=writer) print("echo hi", file=writer) os.chmod(script_path, 0o511) subprocess.run([script_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
内容的提问来源于stack exchange,提问作者cel
相关产品推荐
相关产品推荐

