通过实例解析python subprocess模块原理及用法


Posted in Python onOctober 10, 2020

一、subprocess以及常用的封装函数

运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。
subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

subprocess.call()

父进程等待子进程完成

返回退出信息(returncode,相当于Linux exit code)

subprocess.check_call()

父进程等待子进程完成

返回0

检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查

subprocess.check_output()

父进程等待子进程完成

返回子进程向标准输出的输出结果

检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。

这三个函数的使用方法相类似,下面来以subprocess.call()举例说明:

代码如下:

>>> import subprocess
>>> retcode = subprocess.call(["ls", "-l"])
#和shell中命令ls -a显示结果一样
>>> print retcode
0

将程序名(ls)和所带的参数(-l)一起放在一个表中传递给subprocess.call()

shell默认为False,在Linux下,shell=False时, Popen调用os.execvp()执行args指定的程序;shell=True时,如果args是字符串,Popen直接调用系统的Shell来执行args指定的程序,如果args是一个序列,则args的第一项是定义程序命令字符串,其它项是调用系统Shell时的附加参数。

上面例子也可以写成如下:

代码如下:

>>> retcode = subprocess.call("ls -l",shell=True)

在Windows下,不论shell的值如何,Popen调用CreateProcess()执行args指定的外部程序。如果args是一个序列,则先用list2cmdline()转化为字符串,但需要注意的是,并不是MS Windows下所有的程序都可以用list2cmdline来转化为命令行字符串。

subprocess.Popen()

代码如下:

class Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

实际上,上面的几个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。

与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block),举例:

代码如下:

>>> import subprocess
>>> child = subprocess.Popen(['ping','-c','4','blog.linuxeye.com'])
>>> print 'parent process'

从运行结果中看到,父进程在开启子进程之后并没有等待child的完成,而是直接运行print。

对比等待的情况:

代码如下:

>>> import subprocess
>>> child = subprocess.Popen('ping -c4 blog.linuxeye.com',shell=True)
>>> child.wait()
>>> print 'parent process'

从运行结果中看到,父进程在开启子进程之后并等待child的完成后,再运行print。
此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:代码如下:

child.poll() # 检查子进程状态
child.kill() # 终止子进程
child.send_signal() # 向子进程发送信号
child.terminate() # 终止子进程

子进程的PID存储在child.pid

二、子进程的文本流控制

子进程的标准输入、标准输出和标准错误如下属性分别表示:

代码如下:

child.stdin
child.stdout
child.stderr

可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe),如下2个例子:

代码如下:

>>> import subprocess
>>> child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
>>> print child1.stdout.read(),
#或者child1.communicate()
>>> import subprocess
>>> child1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)
>>> child2 = subprocess.Popen(["grep","0:0"],stdin=child1.stdout, stdout=subprocess.PIPE)
>>> out = child2.communicate()

subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成

subprocess 模块首先推荐使用的是它的 run 方法,更高级的用法可以直接使用 Popen 接口。

run 方法语法格式如下:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)

  • args:表示要执行的命令。必须是一个字符串,字符串参数列表。
  • stdin、stdout 和 stderr:子进程的标准输入、输出和错误。其值可以是 subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。subprocess.PIPE 表示为子进程创建新的管道。subprocess.DEVNULL 表示使用 os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。
  • timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出 TimeoutExpired 异常。
  • check:如果该参数设置为 True,并且进程退出状态码不是 0,则弹 出 CalledProcessError 异常。
  • encoding: 如果指定了该参数,则 stdin、stdout 和 stderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。
  • shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
  • run 方法调用方式返回 CompletedProcess 实例,和直接 Popen 差不多,实现是一样的,实际也是调用 Popen,与 Popen 构造函数大致相同,例如:

实例

#执行ls -l /dev/null 命令
>>> subprocess.run(["ls", "-l", "/dev/null"])
crw-rw-rw- 1 root wheel 3, 2 5 4 13:34 /dev/null
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0)

returncode: 执行完子进程状态,通常返回状态为0则表明它已经运行完毕,若值为负值 "-N",表明子进程被终。

简单实例:

实例

import subprocess
def runcmd(command):
  ret = subprocess.run(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8",timeout=1)
  if ret.returncode == 0:
    print("success:",ret)
  else:
    print("error:",ret)
runcmd(["dir","/b"])#序列参数
runcmd("exit 1")#字符串参数

输出结果如下:

success: CompletedProcess(args=['dir', '/b'], returncode=0, stdout='test.py\n', stderr='')
error: CompletedProcess(args='exit 1', returncode=1, stdout='', stderr='')

Popen() 方法

Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。

构造函数:

class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True, 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)

常用参数:

args:shell命令,可以是字符串或者序列类型(如:list,元组)

bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。

0:不使用缓冲区

1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式

正数:表示缓冲区大小

负数:表示使用系统默认的缓冲区大小。

  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
  • cwd:用于设置子进程的当前目录。
  • env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。

创建一个子进程,然后执行一个简单的命令:

实例

>>> import subprocess
>>> p = subprocess.Popen('ls -l', shell=True)
>>> total 164
-rw-r--r-- 1 root root 133 Jul 4 16:25 admin-openrc.sh
-rw-r--r-- 1 root root 268 Jul 10 15:55 admin-openrc-v3.sh
...
>>> p.returncode
>>> p.wait()
0
>>> p.returncode

这里也可以使用 p = subprocess.Popen(['ls', '-cl']) 来创建子进程。

Popen 对象方法

  • poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。
  • wait(timeout): 等待子进程终止。
  • communicate(input,timeout): 和子进程交互,发送和读取数据。
  • send_signal(singnal): 发送信号到子进程 。
  • terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。
  • kill(): 杀死子进程。发送 SIGKILL 信号到子进程。

实例

import time
import subprocess

def cmd(command):
  subp = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8")
  subp.wait(2)
  if subp.poll() == 0:
    print(subp.communicate()[1])
  else:
    print("失败")
cmd("java -version")
cmd("exit 1")

输出结果如下:

java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

失败

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python进阶教程之文本文件的读取和写入
Aug 29 Python
python网络编程之读取网站根目录实例
Sep 30 Python
Python中的pprint折腾记
Jan 21 Python
python让图片按照exif信息里的创建时间进行排序的方法
Mar 16 Python
Python算法应用实战之队列详解
Feb 04 Python
Python导入模块时遇到的错误分析
Aug 30 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
Python对Tornado请求与响应的数据处理
Feb 12 Python
Python字符串hashlib加密模块使用案例
Mar 10 Python
DjangoWeb使用Datatable进行后端分页的实现
May 18 Python
python如何设置静态变量
Sep 07 Python
matplotlib部件之套索Lasso的使用
Feb 24 Python
如何使用python自带IDLE的几种方法
Oct 10 #Python
Python的3种运行方式:命令行窗口、Python解释器、IDLE的实现
Oct 10 #Python
python如何利用Mitmproxy抓包
Oct 10 #Python
python批量生成条形码的示例
Oct 10 #Python
python如何对链表操作
Oct 10 #Python
Python timeit模块原理及使用方法
Oct 10 #Python
python实现自动打卡的示例代码
Oct 10 #Python
You might like
使用PHP和XSL stylesheets转换XML文档
2006/10/09 PHP
一个简易需要注册的留言版程序
2006/10/09 PHP
PHP求小于1000的所有水仙花数的代码
2012/01/10 PHP
zend framework框架中url大小写问题解决方法
2014/08/19 PHP
PHP防止sql注入小技巧之sql预处理原理与实现方法分析
2019/12/13 PHP
PHP 对象接口简单实现方法示例
2020/04/13 PHP
PHP unset函数原理及使用方法解析
2020/08/14 PHP
js loading加载效果实现代码
2009/11/24 Javascript
基于jQuery的图片大小自动适应实现代码
2010/11/17 Javascript
修复IE9&safari 的sort方法
2011/10/21 Javascript
jquery Moblie入门—hello world的示例代码学习
2013/01/08 Javascript
iframe调用父页面函数示例详解
2014/07/17 Javascript
c#+jquery实现获取radio和checkbox的值
2020/09/12 Javascript
JavaScript前补零操作实例
2015/03/11 Javascript
jquery如何获取元素的滚动条高度等实现代码
2015/10/19 Javascript
[原创]Javascript 实现广告后加载 可加载百度谷歌联盟广告
2016/05/11 Javascript
JavaScript的ExtJS框架中数面板TreePanel的使用实例解析
2016/05/21 Javascript
jQuery控制元素隐藏和显示
2017/03/03 Javascript
JavaScript运动框架 链式运动到完美运动(五)
2017/05/18 Javascript
详解vuex的简单todolist例子
2019/07/14 Javascript
基于vue-cli3创建libs库的实现方法
2019/12/04 Javascript
[46:49]完美世界DOTA2联赛PWL S3 access vs Rebirth 第二场 12.19
2020/12/24 DOTA
跟老齐学Python之传说中的函数编写条规
2014/10/11 Python
python pandas时序处理相关功能详解
2019/07/03 Python
python3获取当前目录的实现方法
2019/07/29 Python
HTML5实现桌面通知 提示功能
2017/10/11 HTML / CSS
SKECHERS官方旗舰店:美国舒适运动休闲品牌
2017/12/22 全球购物
红色连衣裙精品店:Red Dress Boutique
2018/08/11 全球购物
自我评价怎么写正确呢?
2013/12/02 职场文书
高三自我评价
2014/02/01 职场文书
实验室标语
2014/06/21 职场文书
学雷锋宣传标语
2014/06/25 职场文书
2014年军人思想汇报范文
2014/10/12 职场文书
2014年少先队工作总结
2014/12/03 职场文书
2015年学雷锋活动总结
2015/02/06 职场文书
暂停营业通知
2015/04/25 职场文书