subprocess:python中执行外部命令

1. 简介

subprocess 是 Python 的一个标准库,它允许你启动新的进程、连接到它们的输入/输出/错误管道,并且获取它们的返回值。
该库使 Python 程序能够方便地与操作系统交互,执行系统命令,调用外部程序,处理子进程,使得在 Python 程序中运行外部命令和程序变得简单和灵活

2. 主要方法

  • subprocess.run():运行指定的命令并等待其完成,返回一个 CompletedProcess 实例。
  • subprocess.Popen():创建一个新的进程,可以进行更复杂的交互;
  • subprocess.call():运行命令并等待其完成,命令出错时抛出异常;
  • subprocess.check_call():运行命令并等待其完成,命令返回非零退出状态时抛出异常;
  • subprocess.check_output():运行命令,捕获输出,如果命令失败则抛出异常。

注意:
subprocess.run() 默认情况下不会捕获实时输出,它会等待命令执行完成后才返回。
subprocess.Popen() 允许你实时地从 stdout 和 stderr 中读取输出,但要写一个简单的循环代码,这个循环会持续从 stdout 中读取,直到没有更多输出,并且进程已经结束。

3. 简单实例

import subprocess

3.1. 执行ls -l命令

# 执行 ls -l 命令
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)

# 打印输出结果
print("STDOUT:", result.stdout)
if result.stderr:  # 如果存在错误则输出
    print("STDERR:", result.stderr)
image.png

3.2. 执行echo命令并传递参数

# 执行 echo 命令并传递字符串参数
result = subprocess.run(['echo', 'Hello, World!'], capture_output=True, text=True)

# 打印输出结果
print("STDOUT:", result.stdout)
image.png

3.3. 在 Python 脚本中执行其他 Python 脚本

# 执行另一个 Python 脚本
result = subprocess.run(['python', 'other_script.py'], capture_output=True, text=True)

# 打印其他脚本的输出
print("STDOUT:", result.stdout)

3.4. 实时执行命令并获取输出

# 实时执行 tail -f 命令(假设有一个日志文件 log.txt)
with subprocess.Popen(['tail', '-f', 'log.txt'], stdout=subprocess.PIPE, text=True) as process:
    for line in process.stdout:
        print(line.strip())  # 实时打印每一行输出
        # 如果需要停止实时输出,可以使用 process.terminate() 或 process.kill()

4. 复杂实例

4.1 非实时输出中间信息

LigandMPNN是一个项目的根路径,内部包含了可执行python文件run.py,其命令行模式的执行方式如下:

conda activate 对应python环境  # 进入针对该项目的python环境(如果有的话)

cd /home/houliya/LigandMPNN  # 进入项目根路径

# 直接linux终端执行或把下面代码写入shell文件再执行
python run.py \
        --model_type "ligand_mpnn" \
        --seed 111 \
        --pdb_path "/home/houliya/protein_desig_cgx/output_cgx/output_luciferase_RFDAA_all_new/nnluz_RFAA_RFDAA_4.pdb" \
        --out_folder "/home/houliya/protein_desig_cgx/output_test" \
        --pack_side_chains 1 \
        --number_of_packs_per_design 2 \
        --pack_with_ligand_context 1 \
        --file_ending "_ligandMPNN" \
        --batch_size 1 \
        --number_of_batches 2

如何将其在python中执行这些代码,并得到相同的结果?由于不需要输出实时的计算信息,因此利用subprocess.run()方法:

result = subprocess.run(['python','run.py',
                        "--model_type", "ligand_mpnn",
                        "--seed", "111",
                        "--pdb_path", "/home/houliya/protein_desig_cgx/output_cgx/output_luciferase_RFDAA_all_new/nnluz_RFAA_RFDAA_4.pdb",
                        "--out_folder", "/home/houliya/protein_desig_cgx/output_test",
                        "--pack_side_chains", "1",
                        "--number_of_packs_per_design", '2',
                        "--file_ending", "_ligandMPNN",
                        "--batch_size", "1",
                        "--number_of_batches", "2"], capture_output=True, text=True)

if result.stderr:  # 判断是否有错误信息
    print(result.stderr)

if result.stdout:  # 输出一些结果信息
    print(result.stdout)

if result.check_returncode:  # 输出一些返回信息
    print(result.check_returncode)

4.2 实时输出计算过程中的每一步信息

rf_diffusion_all_atom是一个项目的根路径,该项目包含了一个名为rf_se3_diffusion.sif的apptainer容器文件,要执行对应的python代码run_inference.py,必须先启动容器,且代码执行过程中会实时输出相应信息,因此整体比例4.1更复杂,先来看常规命令行的执行方式:

conda activate 对应python环境  # 进入针对该项目的python环境(如果有的话)

cd /home/houliya/rf_diffusion_all_atom  # 进入项目根路径

# 直接linux终端执行或把下面代码写入shell文件再执行
apptainer run --nv rf_se3_diffusion.sif -u run_inference.py \
        inference.deterministic=True \
        diffuser.T=100 \
        inference.output_prefix=/home/houliya/protein_desig_cgx/output_test/nnluz_RFAA_RFDAA \
        inference.input_pdb=/home/houliya/protein_desig_cgx/input_cgx/input_luciferase/nnluz_RFAA.pdb \
        contigmap.contigs=[\'266-266\'] \
        inference.ligand=LG1 \
        inference.num_designs=500 \
        inference.design_startnum=1

如何将其在python中执行这些代码,并得到相同的结果?由于需要输出实时的计算信息,因此利用subprocess.Popen()方法:

process = subprocess.Popen(['apptainer','run',
                        "--nv", "rf_se3_diffusion.sif",
                        "-u", "run_inference.py",
                        "inference.deterministic=True",
                        "diffuser.T=100",
                        "inference.output_prefix=/home/houliya/protein_desig_cgx/output_test/nnluz_RFAA_RFDAA",
                        "inference.input_pdb=/home/houliya/protein_desig_cgx/input_cgx/input_luciferase/nnluz_RFAA.pdb",
                        "contigmap.contigs=[\'266-266\']",
                        "inference.ligand=LG1",
                        "inference.num_designs=500",
                        "inference.design_startnum=1"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)


# 循环读取输出直到没有更多输出
while True:
    output = process.stdout.readline()
    if output == '' and process.poll() is not None:
        break
    if output:
        print(output.strip())  # 打印输出,去除末尾的换行符

# 等待进程结束
stdout, stderr = process.communicate()

# 检查是否有错误输出
if stderr:
    print("STDERR:")
    print(stderr)
image.png

5. 需要注意的地方:

从shell代码转换到subprocess.run()或subprocess.Popen()代码时的输入参数的写法。
例如shell中某个输入参数写为--seed 111,而在subprocess.run()中写成了"--seed", "111"
又例如在shell中某个输入参数写为diffuser.T=100,而在subprocess.Popen()中写成了"diffuser.T=100"
subprocess.run()或subprocess.Popen()中只能输入字符串,而不能输入int、float等其他类型!

6. 其他

https://cloud.tencent.com/developer/article/2291501
https://blog.csdn.net/Htojk/article/details/134048843

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容