python3- subprocess模块. 学习记录

本文介绍了 Python 中的 subprocess 模块,详细讲解了如何使用 subprocess.run 方法来启动子进程,以及 Popen 类的各种方法和属性,包括异常处理、进程间通信等高级用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python3. subporcess 子进程管理模块

 简要说明: 有需要用到python调用外部程序命令的同学们, 那么你们请一定选择subprocess库,它给我们工作带来极大的方便也许我这么解释不正确, 还是用官方的话来说吧 <subporcess 模块允许你产生新的进程,并且可以把输入,输出, 错误 直接连接到管道,最后获取结果,python也有俩个比较功能不是那么太全的内置模块<os.system, os.spawn..>等这些,总之这家伙的出现就是代替那些的,这俩天正用它写了一个小东西,有必要记录下过程. <英文好的同志:  官方文档 >

18.1.0

      用法介绍:

    在python2.x的用法和python3.x有所不一样,本文将介绍基于python3.x版 subprocess简要用法,3x中增加了一些新的特性,如果保留与旧版本兼容,看管还是参阅旧版API.

    3x.中增加了新的方法,例如 subprocess.run()方法. 此方法作用:  使用参数运行命令并返回完整的进程实例

subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs)

此调用方式返回CompletedProcess实例,和直接Popen 差不多,实现是一样的,实际也是调用Popen,与Popen构造函数大致相同,例如:

In: subprocess.run(["ls", "-l", "/dev/null"] 
    #执行ls -l /dev/null 命令
Out: CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0)
    #ponpenargs: 命令行参数,类型为list 或 str,返回CompletedProcess实例
    #returncode: 执行完子进程状态,通常返回状态为0则表明它已经运行完毕,弱值为负值"-N",表明子进程被终止.
ponpenargs: 命令行参数,类型为list 或 str,返回CompletedProcess实例
    #returncode: 执行完子进程状态,通常返回状态为0则表明它已经运行完毕,弱值为负值"-N",表明子进程被终止.
 
Out: subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE, stderr=subprocess.PIPE

 

上述介绍新版本特性,再看下通常的一些用法:

列举一些基本用法:

            subprocess.call():  方法call父进程等待子进程完成,返回退出信息。

          subprocess.check_call(): 方法check_call父进程等待子进程完成,返回0,唯一不同区别,它会检查退出信息,如果returncode 不为0,则引发 subprocess.CallProcessError。

         subprocess.check_output(): 方法check_output 父进程等待子进程完成,返回子进程向标准输出的输出结果,它和check_call 同样会检查退出信息,如returncode 不为0,则引发 subprocess.CallProcessError。

最主要还是讲subprocess.Popen() 附上源码,Popen的构造函数

def __init__(self, args, bufsize=-1, executable=None,
                 stdin=None, stdout=None, stderr=None,
                 preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0,
                 restore_signals=True, start_new_session=False,
                 pass_fds=(), *, encoding=None, errors=None):

举个小例子:

In [31]: import shlex
In [32]: command_line
Out[32]: '/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo \'$MONEY\'"'
In [33]: args = shlex.split(command_line)
In [34]: print(args)
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
p = subprocess.Popen(args=args)

    shlex: POSIX模式下的一个解析命令行参数模块

特别注意一点,shell中是由空白分割,例如这里的-input 和 参数 eggs.txt 将单独放到列表元素里,而在shel中使用时需要引用或者反斜杠转义参数。

在POSIX模式上shell=True,shell默认为/bin/sh,如果args默认是一个字符串,则该字符串指定通过shell方式执行命令,这意味着该字符串的格式必须与shell下输入的格式一致。

在3.x 中 提供上下文管理,通过with语句支持Popen对象作为上下文管理,在退出时,关闭文件描述符,并等待进程。

In [35]: with subprocess.Popen(['ifconfig'], stdout=subprocess.PIPE) as proc:
    ...:     log.write(proc.stdout.read()) 

在python3.6中更改: ResourceWarning 如果子进程仍在运行,Popen析够函数会发出警告.

18.1.1

        异常情况

程序在执行中,子进程中引发异常将在父进程中提示,并且异常对象会有一个额外属性调用,child_traceback() ,此方法来自子进程回朔信息。

最常见的异常:

                OSError:  尝试执行一个不存在的命令或者文件引发此异常

                ValueError:  Popen中使用无效的参数调用,引发异常

                CalledProcessError: 如果子进程返回非0的状态码,会引发此异常 <check_all,check_output>

                TimeoutExpired: 作用于超时参数引发,例如: 如果在退出之前超时,Popen.communicate() 则引发异常

    所有异常均继承自 SubprocessError 基类.

18.1.1

        Popen对象详解

Popen 类的实例具有一下方法:

Popen.poll (): 检查子进程是否已终止,设置并返回returncode属性,否则返回None

Popen.wait (timeout=None): 等待子进程终止,设置并返回returncode属性,如果进程在超时间隔内没有终止,则引发TimeoutExpried异常,可捕获次异常重新等待。<注意: 这将使用时死锁stdout=PIPE或者stderr=PIPE和子进程结果输出到管道,使它阻止等待os缓冲器接受更多数据,使用:Popen.communicate() 可避免这种情况

Popen.communicate(input=None, timeout=None):  与子进程交互,将数据发送到"管道"stdin,然后从stdout 或者 stderr中读取全部数据,再然后等待进程终止,communicate()返回一个元组,在文本模式下打开流,则数据是则数据是str,否则是Bytes  ****PS返回形式:(stdout_data, stderr_date)

    我们如果想要将数据发送到sitdin,这么可以干,在创建Popen对象时候,传入stdin=subprocess.PIPE  |  stdout=subprocess.PIPE  | stderr=subprocess.PIPE  输入 | 输出 | 错误信息

这里再说一个重点,若进程超时没有终止,捕获TimeoutExpired异常重新通信不会丢失任何输出结果

如超时,子进程不会自动终止,因此为了清理残留子进程,可以这么做

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

又要说一下,这种方式,读取的数据缓存在内存中,如果数据量很大,建议不要使用这种这种方式,还好在3.x版中增加了超时选项

Popen.send_signal (signal): 向子进程发送信号,<注意: windows 上的为 terminate()>

Popen.terminate (): 停止子进程,<注意: windows 上调用Win32 API TerminateProcess()>

Popen.kill (): 杀死子进程

 

Popen.args (): 传递给Popen的参数,可以是列表 或者 字符串

 

Popen.stdin(): 如果stdin 参数是PIPE,返回可写流对象,如果指定encoding等参数为True,怎是文本流,否则字节流,如果stdin的参数不是PIPE,返回None

 

Popen.stdout (): 如果stdin 参数是PIPE,从流中读取子进程标准输出,如果指定encoding等参数为True,怎是文本流,否则字节流,如果stdin的参数不是PIPE,返回None

 

Popen.stdout (): 如果stdin 参数是PIPE,从流中读出子进程错误输出,如果指定encoding等参数为True,怎是文本流,否则字节流,如果stdin的参数不是PIPE,返回None

 

Popen.pid (): 返回子进程ID ,PS: (如果将shell=True,则是生成shell的进程ID)

 

Popen.returncode ():  返回子进程的状态码.

其他用法更具自己需求可自行查看官方文档,边看边试..

 

##########################################################################

 

至此,自己用到subprocess基本用法总结完了,记录一下,记忆犹新,很好用的一个模块,come on ..   对,用到这个童鞋,可以在看看shlex模块,很简单的一个命令行解析模块,配合使用更便捷。

<think>嗯,用户之前问过关于JMeter测试报告显示每次请求结果的问题,现在又接着问如何用Python自动化执行JMeter命令生成CSV和报告。看起来用户可能是在做自动化测试集成,需要把JMeter的执行和报告生成整合到Python脚本中,可能是为了CI/CD流程或者定时任务。 首先,我需要确认用户的需求是什么。他们可能希望用Python脚本代替手动执行命令行命令,这样可以在自动化测试流程中更方便。用户给出的命令是jmeter -n -t test.jmx -l results.csv && jmeter -g results.csv -o report/,所以需要把这个命令用Python来执行,并且处理可能的错误。 接下来,我要考虑如何用Python执行外部命令。常用的方法有os.system、subprocess模块。但subprocess更灵活,可以获取输出和错误信息,处理返回码,所以推荐用subprocess.run或者subprocess.Popen。 然后,需要处理命令的顺序执行,即第二个命令在第一个成功后执行。在Shell中可以用&&,但在Python中需要检查第一个命令的返回码是否为0,然后决定是否执行第二个。这可以通过subprocess的check参数或者手动检查返回码来实现。 另外,用户可能需要错误处理,比如命令执行失败时的异常捕获,日志记录,以及生成报告后的后续处理,比如压缩报告或者发送通知。这部分可能需要进一步扩展,但根据用户当前的命令,可能暂时不需要,但最好在示例中体现出来,供用户参考。 还要考虑路径问题,比如test.jmx、results.csv和report目录的位置是否在脚本当前目录,或者是否需要指定绝对路径。在示例中可能需要提醒用户注意路径的正确性,或者使用变量来管理路径。 然后,示例代码的结构应该是:执行第一个JMeter命令,检查是否成功,成功则执行第二个生成报告的命令。使用subprocess模块,设置shell=True(注意安全性问题,但用户给出的命令里有&&,可能需要shell来解析),或者分开执行两个命令,并检查各自的返回码。 同时,需要处理可能出现的异常,比如JMeter不在系统路径中,或者test.jmx文件不存在,这时候subprocess会抛出CalledProcessError,需要进行捕获并提示用户。 最后,用户可能希望脚本能够输出执行日志,记录每个步骤的结果,方便调试和查看执行情况。所以在示例代码中加入打印语句或者日志模块的使用会更好。 总结下来,回答需要包括以下几个部分: 1. 使用subprocess模块执行命令,推荐subprocess.run。 2. 分步执行两个JMeter命令,并检查每个步骤的成功与否。 3. 错误处理,捕获异常并提供提示信息。 4. 路径处理,确保脚本能找到JMeter和测试计划文件。 5. 可能的扩展点,如日志记录、后续处理等。 可能需要提醒用户注意JMeter的环境变量是否配置正确,或者在脚本中指定JMeter的绝对路径,避免因环境问题导致命令执行失败。此外,生成的报告目录需要为空,否则JMeter会报错,所以可能需要添加删除现有报告目录的逻辑。 现在,根据这些思路,整理出一个结构清晰的回答,分步骤说明如何用Python实现自动化执行JMeter命令并生成报告,同时包含代码示例和注意事项。</think>以下是使用Python实现JMeter自动化测试及报告生成的完整解决方案: --- ### 一、基础执行脚本 ```python import subprocess import shutil # 路径配置 JMETER_PATH = "jmeter" # 如果未配置环境变量,需指定绝对路径如:"/opt/jmeter/bin/jmeter" TEST_PLAN = "test.jmx" RESULTS_CSV = "results.csv" REPORT_DIR = "report/" def run_jmeter(): try: # 清理历史报告目录 shutil.rmtree(REPORT_DIR, ignore_errors=True) # 执行压测命令 cmd_run = f"{JMETER_PATH} -n -t {TEST_PLAN} -l {RESULTS_CSV}" print(f"执行压测命令: {cmd_run}") subprocess.run(cmd_run, check=True, shell=True) # 生成HTML报告 cmd_report = f"{JMETER_PATH} -g {RESULTS_CSV} -o {REPORT_DIR}" print(f"生成报告命令: {cmd_report}") subprocess.run(cmd_report, check=True, shell=True) print("自动化测试执行完成") return True except subprocess.CalledProcessError as e: print(f"命令执行失败,错误码:{e.returncode}") print(f"错误输出:{e.output}") return False if __name__ == "__main__": run_jmeter() ``` --- ### 二、增强功能实现 #### 1. 执行时间统计 ```python import time def run_jmeter(): start_time = time.time() # ...原有代码... duration = time.time() - start_time print(f"总耗时:{duration:.2f}秒") ``` #### 2. 邮件通知 ```python import smtplib from email.mime.text import MIMEText def send_email(status): msg = MIMEText(f"JMeter测试执行状态:{&#39;成功&#39; if status else &#39;失败&#39;}") msg[&#39;Subject&#39;] = &#39;自动化测试通知&#39; msg[&#39;From&#39;] = &#39;sender@example.com&#39; msg[&#39;To&#39;] = &#39;receiver@example.com&#39; with smtplib.SMTP(&#39;smtp.example.com&#39;, 587) as server: server.starttls() server.login(&#39;user&#39;, &#39;password&#39;) server.send_message(msg) ``` --- ### 三、关键参数配置 通过字典管理所有配置项: ```python CONFIG = { "jmeter_path": "/usr/local/bin/jmeter", "test_plan": "stress_test.jmx", "result_file": "results_$(date +%s).csv", # 时间戳防覆盖 "report_dir": "reports/latest/", "java_opts": "-Xms2g -Xmx4g" # JVM内存设置 } ``` --- ### 四、异常处理优化 ```python try: # ...执行命令... except FileNotFoundError: print("错误:JMeter路径配置错误") except PermissionError: print("错误:无文件写入权限") except KeyboardInterrupt: print("用户手动终止操作") except Exception as e: print(f"未捕获异常:{str(e)}") ``` --- ### 五、跨平台兼容方案 使用`platform`模块自动适配路径: ```python import platform def get_jmeter_path(): system = platform.system() if system == "Windows": return "C:\\jmeter\\bin\\jmeter.bat" elif system == "Linux": return "/opt/jmeter/bin/jmeter" elif system == "Darwin": return "/usr/local/Cellar/jmeter/5.4.1/bin/jmeter" else: raise OSError("Unsupported operating system") ``` --- ### 六、最佳实践建议 1. **日志记录** 添加日志记录功能: ```python import logging logging.basicConfig( filename=&#39;jmeter_auto.log&#39;, level=logging.INFO, format=&#39;%(asctime)s - %(levelname)s - %(message)s&#39; ) ``` 2. **性能监控** 集成资源监控: ```python import psutil def monitor_system(): print(f"CPU使用率:{psutil.cpu_percent()}%") print(f"内存使用:{psutil.virtual_memory().percent}%") ``` 3. **自动清理** 设置结果文件保留策略: ```python def cleanup(max_files=5): files = sorted(os.listdir(), key=os.path.getctime) for f in files[:-max_files]: if f.startswith("results_") and f.endswith(".csv"): os.remove(f) ``` --- ### 七、完整调用示例 ```python if __name__ == "__main__": if run_jmeter(): send_email(True) cleanup() print("查看报告:", os.path.abspath(REPORT_DIR)) else: send_email(False) print("执行失败,请检查日志") ``` --- 通过以上方案可以实现: - 完整的JMeter测试自动化流程 - 执行状态实时监控 - 跨平台兼容性 - 异常处理与通知机制 - 历史数据管理 建议根据实际需求选择功能模块组合使用,生产环境建议增加Docker容器化部署方案以提高环境一致性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值