You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python subprocess顺序执行配置脚本获取PID及返回码异常问题求助

问题解答

修正后完整代码

#! /usr/bin/python3
import configparser
import argparse as ap
import subprocess
import sys
import shlex
import os
import signal

# 存储运行中的子进程对象,方便stop时调用
running_procs = []

def file_reader():
    configParser = configparser.ConfigParser()
    configParser.read('config.ini')
    # 按配置文件顺序返回所有命令行,不需要反转字典
    cmd_list = []
    for section in configParser.sections():
        # 按path1、path2、path3的顺序拿命令
        for _, cmd in sorted(configParser.items(section), key=lambda x: x[0]):
            cmd_list.append(cmd)
    return cmd_list

def kill_process():
    global running_procs
    for proc in running_procs:
        try:
            if proc.poll() is None: # 进程还在运行
                proc.kill()
                print(f"已杀死进程PID: {proc.pid}")
        except Exception as e:
            print(f"杀死进程PID {proc.pid}失败: {str(e)}")
    running_procs.clear()

def main():
    cmd_list = file_reader()
    parser = ap.ArgumentParser()
    parser.add_argument('-s', '--start', help='start script', action='store_true')
    parser.add_argument('-q', '--stop', help='stop script', action='store_true')
    args = parser.parse_args()

    if args.start and args.stop:
        print("error: 不能同时指定start和stop参数")
        sys.exit(1)
    elif args.start:
        for idx, cmd in enumerate(cmd_list, 1):
            print(f"正在执行第{idx}条命令: {cmd}")
            try:
                # 拆分命令为参数列表,关闭shell更安全,避免参数解析错误
                cmd_args = shlex.split(cmd)
                # 用Popen启动进程,可获取PID
                proc = subprocess.Popen(cmd_args, close_fds=True)
                running_procs.append(proc)
                print(f"进程启动成功,PID: {proc.pid}")
                # 等待进程结束,设置超时时间(单位秒,可自行调整),超时会抛出TimeoutExpired异常
                return_code = proc.wait(timeout=3600)
                if return_code != 0:
                    if return_code < 0:
                        print(f"进程PID {proc.pid} 被信号杀死,信号编号: {-return_code}")
                    else:
                        print(f"进程PID {proc.pid} 执行结束,返回码: {return_code}")
                    # 如果需要某条命令失败就终止后续执行,打开下面的注释
                    # kill_process()
                    # sys.exit(return_code)
                else:
                    print(f"进程PID {proc.pid} 执行成功,返回码0")
            except subprocess.TimeoutExpired:
                print(f"进程PID {proc.pid} 执行超时,强制杀死")
                proc.kill()
                kill_process()
                sys.exit(1)
            except Exception as e:
                print(f"命令执行出错: {str(e)}")
                kill_process()
                sys.exit(1)
        print("\n所有命令执行完成")
        sys.exit(0)
    elif args.stop:
        kill_process()
        sys.exit(0)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        kill_process()
        exit()

问题逐一说明

  • 问题1、2(顺序执行+获取PID)解决:
    原代码使用的subprocess.call是阻塞接口,只会返回进程退出码,无法获取进程PID。改用subprocess.Popen启动进程,可直接通过Popen.pid属性拿到进程ID。启动每个进程后调用wait()方法等待当前进程执行完成再启动下一个,即可实现按配置顺序逐个运行。如果进程运行卡住,可通过wait(timeout=xxx)设置超时时间,超时后直接调用proc.kill()杀死对应进程,也可以通过存储的PID手动执行kill操作。
  • 问题3(返回码始终为1)解决:
    该问题由两处错误导致:
    1. 传参逻辑错误:subprocess.call([k], shell=True)的写法中,开启shell=True时如果传入列表,只会将列表第一个元素作为命令执行,后续元素会被当作shell的参数而非命令的参数,你配置里的带参数的长命令根本没有正确执行,自然返回非0的错误码。修正方案是用shlex.split()将完整命令字符串拆分成参数列表,关闭shell=True,既避免参数解析错误,也更安全。
    2. 字典反转逻辑错误:原代码将配置的path到命令的字典反转为命令到path的字典,Python3.7之前的版本字典默认不保留插入顺序,会导致命令执行顺序和配置顺序不一致,甚至出现命令匹配错误的问题,直接按配置的key排序读取命令即可,无需反转字典。

内容的提问来源于stack exchange,提问作者Asad Javed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 01:24:03