如何用Python多次调用Fortran可执行文件且无需重复创建shell进程
问题描述
我需要从Python中运行一个已编译的Fortran数值模型,使用F2PY编译该程序需要对Fortran例程做大量修改,实现复杂度过高,因此选择通过subprocess模块直接调用其可执行文件。
当前需要将该程序调用数千次,发现每次调用都生成新shell的实现方式大幅拖慢了整体运行效率。我当前的实现代码如下(暂无法提供可复现示例):
import os import subprocess foo_path = '/path/to/compiled/program/' program_dir = os.path.join(foo_path, "FOO") # FOO是Fortran可执行文件 instruction = program_dir + " < nlst" # 需要给FOO传入一个名为nlst的文本配置文件 subprocess.call(instruction, shell=True, cwd=foo_path) # 运行可执行文件
这种放在循环中运行的方式功能正常,FOO会生成文本输出文件供Python读取,但我希望可以保持shell处于激活状态,每次仅向其传入nlst配置文件的路径即可运行程序;另一种可选方案是启动一个空闲shell进程,等待接收类似./FOO < nlst的指令字符串再执行,但我不确定具体要如何实现,请问有没有可行的方案?
[补充]我尝试过如下写法,但communicate方法调用后会终止process进程,无法执行第二次调用:
from subprocess import Popen, PIPE foo_path = '/path/to/FOO/' process = Popen(['bash'], stdin=PIPE, cwd=foo_path) process.communicate(b'./FOO < nlst')
可行方案
communicate方法的设计逻辑就是等待子进程终止后再返回,同时会自动关闭标准输入流,因此确实无法支持多次调用的需求,你可以直接向常驻shell进程的stdin管道逐行写入执行命令,核心实现逻辑如下:
- 启动常驻bash进程时,同时打开stdout和stderr管道,方便后续判断命令执行状态
- 每写入一条完整命令后添加换行符
\n,让shell识别到指令结束 - 写入后手动调用flush方法清空缓冲区,确保命令立刻被shell接收执行
示例代码如下:
from subprocess import Popen, PIPE import time foo_path = '/path/to/FOO/' # 启动常驻bash进程,设置文本模式更方便操作字符串,不需要手动转字节 process = Popen(['bash'], stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True, cwd=foo_path) # 你要循环执行的nlst文件列表 nlst_list = ["nlst1", "nlst2", "nlst3"] # 替换为你实际的配置文件列表 for nlst_file in nlst_list: # 写入执行命令,末尾加换行 process.stdin.write(f"./FOO < {nlst_file}\n") # 加一个标记命令,用于判断当前FOO执行完成,避免异步时序问题 process.stdin.write("echo 'CMD_FINISHED'\n") process.stdin.flush() # 读取输出直到遇到我们定义的结束标记,确认命令执行完成 while True: output_line = process.stdout.readline() if output_line.strip() == 'CMD_FINISHED': break # 如果你需要捕获FOO的输出可以在这里处理 # print(output_line) # 这里可以加读取FOO生成的输出文件的逻辑 print(f"{nlst_file} 执行完成") # 所有任务执行完后,给shell发退出命令,正常关闭进程 process.stdin.write("exit\n") process.stdin.flush() process.wait()
额外优化建议
- 如果你每次只是修改nlst里的少量参数,可以直接把配置内容通过stdin传给FOO,不需要每次生成新的nlst文件,能进一步减少磁盘IO开销,速度更快
- 如果FOO本身没有后台运行逻辑,不需要额外加超时判断,上述标记法已经可以稳定判断执行结束
内容的提问来源于stack exchange,提问作者Lacococha
相关产品推荐
相关产品推荐

