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)

3.2. 执行echo命令并传递参数
# 执行 echo 命令并传递字符串参数
result = subprocess.run(['echo', 'Hello, World!'], capture_output=True, text=True)
# 打印输出结果
print("STDOUT:", result.stdout)

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)

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