在python
开发期间,很多时候我们会需要执行一段cmd
终端命令,或者是执行其他程序返回stdout
或者文件输出结果。这种时候,我们就需要用到subprocess
模块。虽然我们用os.system
也可以达到执行命令的需求,但用os.system
只是干发一段命令,对于执行命令的程序,我们没有办法跟踪它的内部状态以及执行结果,因此从稳定性的角度来讲不是一个好的选择。因此,本篇文章讲解下subprocess
子进程模块的的基础应用,让没用过这个模块或是经常踩坑的同学都避避坑。
subprocess
模块的官方文档在这里,最核心的单位是subprocess.Popen
类,它描述了一个正在运行中的进程。subprocess
最基础的用法是subprocess.run
,我们入参一段cmd
终端命令,run
方法内部就会启动一个Popen
对象执行这个命令,等待命令执行结束后,返回这个命令执行的退出码retcode
,标准输出流内容stdout
以及标准错误流内容stderr
。我们可以从源码中详细捋一下subprocess.run
的流程:
1 | def run(*popenargs, |
subprocess.run
是一个阻塞方法,执行了这个接口后,需要等待run
入参的命令执行完才能返回。而有些时候,我们需要单独起一个(进程执行)cmd
命令,然后周期性每几秒钟去检查命令执行的状态,检查完之后我们还可以在主进程干别的事情,也就是搞一个独立出来的进程。这种情况下,subprocess.run
就无法满足,必须直接开subprocess.Popen
。
一个基本的示例代码如下:
1 | import subprocess |
这段程序模拟了周期性等待子进程执行完成的场景,执行完成后拉取stdout
和stderr
打印,执行超时就强杀进程。基本上关键的地方都有注释,如果有其他类似的场景,可以直接照搬代码。
最后我们也能看到,subprocess
本质也是多进程,但和multiprocessing
有所不同,multiprocessing
是多个python
进程,着重于管理多个python
进程的运行时环境以及之间的通信;而subprocess
则是侧重于去跟踪python
程序启动的任意类型进程的状态。两者也有共同点,就是主进程都会持有子进程的handle
,只要没调用类似subprocess.run
这种阻塞获取子进程状态/结果的接口,在起了新进程后,主进程内都能够随时随地去获取子进程的状态信息。