如何在Python中创建临时文件,供Linux终端执行后自动删除?
解决Python临时文件处理OpenRefine转换规则的问题
问题背景
- 需求:将Python中的
formato_db列表保存为临时文件,通过Linux终端执行OpenRefine客户端命令后自动删除该文件 - 原实现:使用
open()函数保存为transformaciones.json文件 - 当前问题:使用
tempfile.SpooledTemporaryFile()时触发错误:TypeError: 'dict' object cannot be interpreted as an integer
错误原因分析
原代码存在多个核心问题:
- 无效类型转换:将
formato_db(列表类型)强行转为bytes,json.dump无法直接序列化字节对象,触发类型错误 - 临时文件生命周期错误:
SpooledTemporaryFile在with块结束后会自动关闭并删除,返回的tfile是已失效的文件对象,无法被后续外部命令调用 - 参数传递错误:
apply_transformation_db函数需要的是临时文件的路径(供docker容器访问),而非文件对象;且调用时错误传入了formato_db而非生成的临时文件 - 工具选型错误:
SpooledTemporaryFile没有实际磁盘路径,无法被外部进程(如docker容器)读取,需使用带文件名的临时文件类
修复方案
使用tempfile.NamedTemporaryFile创建带磁盘路径的临时文件,确保外部进程能访问,同时手动控制文件生命周期:
- 直接序列化
formato_db列表为JSON,跳过无效的字节转换 - 配置
NamedTemporaryFile为delete=False,避免文件关闭时立即删除 - 执行完终端命令后手动删除临时文件,完成自动清理
- 修正参数传递逻辑,传入正确的文件路径
完整修正代码
import tempfile import json import subprocess import os formato_db=[{'op': 'core/column-reorder', 'columnNames': ['\ufeffNúmero de Cliente', '[Account.AccountCode?]', 'CRM Origen', 'Monto inicial', '[ModInitCta?]', '[DeudaRealCuenta?]', '[BillCycleName?]', 'Nombre Campaña', 'Nombre Casa de Cobro', 'Fecha de Asignacion', 'Deuda Gestionable', 'Dirección Completa', 'Fecha Final', 'Email', 'Telefono 1', 'Telefono 2', 'Telefono 3', 'Telefono 4', 'Segmento', '[Documento?]', '[AccStsName?]', 'Ciudad', '[InboxName?]', 'Nombre del Cliente', 'Id de Ejecucion', 'Fecha de Vencimiento', 'Numero Referencia de Pago', 'MIN', 'Plan', 'Precio Subscripcion'], 'description': 'Reorder columns'}, {'op': 'core/column-addition', 'engineConfig': {'facets': [], 'mode': 'row-based'}, 'baseColumnName': 'Telefono 1', 'expression': "join ([coalesce(cells['Telefono 1'].value,''),coalesce(cells['Telefono 2'].value,''),coalesce(cells['Telefono 3'].value,''),coalesce(cells['Telefono 4'].value,'')],' / ')", 'onError': 'keep-original', 'newColumnName': 'Telefonos', 'columnInsertIndex': 15, 'description': "Create column Telefonos at index 15 based on column Telefono 1 using expression join ([coalesce(cells['Telefono 1'].value,''),coalesce(cells['Telefono 2'].value,''),coalesce(cells['Telefono 3'].value,''),coalesce(cells['Telefono 4'].value,'')],' / ')", 'op': 'core/column-reorder', 'columnNames': ['\ufeffNúmero de Cliente', '[Account.AccountCode?]', 'CRM Origen', 'Monto inicial', '[ModInitCta?]', '[DeudaRealCuenta?]', '[BillCycleName?]', 'Nombre Campaña', 'Nombre Casa de Cobro', 'Fecha de Asignacion', 'Deuda Gestionable', 'Dirección Completa', 'Fecha Final', 'Email', 'Telefonos', 'Segmento', '[Documento?]', '[AccStsName?]', 'Ciudad', '[InboxName?]', 'Nombre del Cliente', 'Id de Ejecucion', 'Fecha de Vencimiento', 'Numero Referencia de Pago', 'MIN', 'Plan', 'Precio Subscripcion'], 'description': 'Reorder columns'}, {'op': 'core/column-reorder', 'columnNames': ['\ufeffNúmero de Cliente', '[Account.AccountCode?]', 'CRM Origen', 'Monto inicial', '[ModInitCta?]', '[DeudaRealCuenta?]', '[BillCycleName?]', 'Nombre Campaña', 'Nombre Casa de Cobro', 'Fecha de Asignacion', 'Deuda Gestionable', 'Dirección Completa', 'Fecha Final', 'Email', 'Telefonos', 'Segmento', 'Ciudad', '[InboxName?]', 'Nombre del Cliente', 'Id de Ejecucion', 'Fecha de Vencimiento', 'Numero Referencia de Pago', 'Plan', 'Precio Subscripcion'], 'description': 'Reorder columns'}, {'op': 'core/text-transform', 'engineConfig': {'facets': [], 'mode': 'row-based'}, 'columnName': 'Monto inicial', 'expression': 'value.toNumber()', 'onError': 'keep-original', 'repeat': False, 'repeatCount': 10, 'description': 'Text transform on cells in column Monto inicial using expression value.toNumber()'}, {'op': 'core/text-transform', 'engineConfig': {'facets': [], 'mode': 'row-based'}, 'columnName': 'Dirección Completa', 'expression': 'value.toTitlecase()', 'onError': 'keep-original', 'repeat': False, 'repeatCount': 10, 'description': 'Text transform on cells in column Dirección Completa using expression value.toTitlecase()'}, {'op': 'core/text-transform', 'engineConfig': {'facets': [], 'mode': 'row-based'}, 'columnName': '\ufeffNúmero de Cliente', 'expression': 'grel:value.substring(0, value.length()-2)', 'onError': 'keep-original', 'repeat': False, 'repeatCount': 10, 'description': 'Text transform on cells in column \ufeffNúmero de Cliente using expression grel:value.substring(0, value.length()-2)'}, {'op': 'core/text-transform', 'engineConfig': {'facets': [], 'mode': 'row-based'}, 'columnName': 'Nombre Campaña', 'expression': 'grel:value.substring(4)', 'onError': 'keep-original', 'repeat': False, 'repeatCount': 10, 'description': 'Text transform on cells in column Nombre Campaña using expression grel:value.substring(4)'}] def get_transformations(formato_db): # 创建带.json后缀的临时文件,保留文件名直到手动删除 with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json', encoding='utf-8') as tfile: json.dump(formato_db, tfile, ensure_ascii=False) return tfile.name def apply_transformation_db(proyecto_id, temp_file_path): err = None proyecto_id = str(proyecto_id) cmd = ['sudo', 'docker', 'run', '--rm', '--network=host', '-v', '/home/ubuntu/api:/data:z', 'felixlohmeier/openrefine-client:v0.3.10', '-H', 'localhost', '--apply', temp_file_path, proyecto_id ] # 执行命令并捕获错误信息 result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: err = f"命令执行失败: {result.stderr}" return err # 执行流程 temp_file_path = get_transformations(formato_db) err_transf_application = apply_transformation_db(proyecto_id, temp_file_path) # 清理临时文件 os.unlink(temp_file_path) # 错误处理(可选) if err_transf_application: print(err_transf_application)
关键改动说明
- 临时文件选型:
NamedTemporaryFile生成带磁盘路径的文件,支持外部进程访问;suffix='.json'明确文件类型,适配OpenRefine客户端 - 序列化优化:
ensure_ascii=False保留西班牙语重音等特殊字符,无需额外类型转换 - 生命周期控制:
delete=False避免文件提前删除,执行完命令后用os.unlink手动清理 - 错误捕获:通过
subprocess.run的返回码和stderr捕获命令执行错误,便于排查问题
内容的提问来源于stack exchange,提问作者frankh07
相关产品推荐
相关产品推荐

