【问题标题】:Run a program from python several times whitout initialize different shells多次从 python 运行程序,但不初始化不同的 shell
【发布时间】:2021-12-11 16:30:58
【问题描述】:

我想从 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 is the Fortran executable
instruction = program_dir + " < nlst"                  #It is necesary to provide FOO a text file (nlst)
                                                       #with the configuration to the program

subprocess.call(instruction, shell=True, cwd=foo_path) #run the executable

以这种方式运行它(在循环内),它运行良好,FOO 生成一个文本文件输出,我可以从 python 中读取它。但我想做同样的事情,保持 shell 处于活动状态,并只向它提供"nlst" 文件路径。另一个不错的选择可能是启动一个空 shell 并让它等待instruction 字符串,它看起来像"./FSM &lt; nlst"。但是我不确定该怎么做,有什么想法吗?

谢谢!

[已编辑] 这样的事情应该可以工作,但 .comunicate 结束 process 并且第二次调用不起作用:

from subprocess import Popen, PIPE

foo_path = '/path/to/FOO/'
process = Popen(['bash'], stdin=PIPE, cwd=foo_path)
process.communicate(b'./FOO < nlst')

【问题讨论】:

  • 您可以使用管道(请参阅 popen())来允许您的 python 脚本发送字符串。在管道的另一端,您运行一个执行 read 的 shell 脚本,并使用它刚刚从标准输入读取的参数启动您的 Python 脚本。
  • 也许线程对你有用 - link
  • 谢谢@Ronald。我认为你的方法正是我想要的。但不确定如何实现它。我将尝试编辑问题

标签: python subprocess popen


【解决方案1】:

为了扩展我的评论,这是一个使用您的代码进行线程化的示例:

import os
import subprocess
from concurrent.futures import ThreadPoolExecutor

foo_path = '/path/to/compiled/program/'
program_dir = os.path.join(foo_path, "FOO")            #FOO is the Fortran executable
instruction = program_dir + " < nlst"                  #It is necesary to provide FOO a text file (nlst)
                                                       #with the configuration to the program

def your_function():
    subprocess.call(instruction, shell=True, cwd=foo_path) #run the executable

# create executor object
executor = ThreadPoolExecutor(max_workers=4)  # uncertain of how many workers you might need/want

# specify how often you want to run the function
for i in range(10):
    # start your function as thread
    executor.submit(your_function)

【讨论】:

  • 谢谢,但这并不能解决问题,因为 'your_function' 每次在池中运行时都会创建一个新的 shell。
【解决方案2】:

我使用pexpect 模块找到了这个解决方案,

import pexpect
import os.path

foo_path = '/path/to/FOO/'
out_path = '/path/to/FOO/foo_out_file'  #path to output file
child = pexpect.spawn('bash', cwd=foo_path)
child.sendline('./FOO < nlst')

while not os.path.exists(out_path): #wait until out_path is created
    continue

【讨论】:

    【解决方案3】:

    我在评论中的意思类似于以下 Python 脚本:

    from subprocess import Popen, PIPE
    
    foo_path = '/home/ronald/tmp'
    process = Popen(['/home/ronald/tmp/shexecutor'], stdin=PIPE, cwd=foo_path)
    
    process.stdin.write("ls\n")
    process.stdin.write("echo hello\n")
    process.stdin.write("quit\n")
    

    以及执行命令的shell脚本:

    #!/bin/bash
    
    while read cmdline; do
        if [ "$cmdline" == "quit" ]; then
            exit 0
        fi
        eval "$cmdline" >> x.output
    done
    

    您几乎可以做任何事情,而不是进行“评估”。 请注意,这只是实际实现的概要。 你需要做一些错误处理。如果您要在生产环境中使用它,请务必将代码硬化到极限。

    【讨论】:

    • 这是有道理的。我将尝试在没有 shell 脚本的情况下实现它,但如果我不能,这是一个选项。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    • 2013-05-08
    • 2022-01-05
    相关资源
    最近更新 更多