## 概述
subprocess模块中只定义了一个类:Popen。可以使用Popen来创建进程,并与进程进行复杂的交互。它的构造函数如下。
---
### Popen使用示例
示例1,使用shlex.split先对参数进行处理
```
>>> import shlex, subprocess
>>> command_line = input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print(args)
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # Success!
```
示例2. 直接使用列表作为参数
```
Popen(['/bin/sh', '-c', args[0], args[1], ...])
```
示例3. 这个上下文管理器在python3中才可以使用
```
with Popen(["ifconfig"], stdout=PIPE) as proc:
log.write(proc.stdout.read())
```
---
### subprocess.Popen参数
```
subprocess.Popen(args, bufsize=0, executable=None, shell=False, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False,cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
```
`参数args`:**字符串**或者**序列类型**(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。
`参数bufsize`:指定缓冲。
`参数executable`: 用于指定可执行程序。一般情况下我们通过args参数来设置所要运行的程序。如果将参数shell设为True,executable将指定程序使用的shell。
`参数stdin, stdout, stderr`: 分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示`从父进程继承`。
`参数preexec_fn`: 只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。
`参数shell`: 设为true,程序将通过shell来执行。
`参数cwd`: 用于设置子进程的当前目录。
`参数env`: 是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
`参数Universal_newlines`: 不同操作系统下,文本的换行符是不一样的。如:windows下用'\r\n'表示换,而Linux下用'\n'。如果将此参数设置为True,Python统一把这些换行符当作'\n'来处理。
---
### subprocess.Popen的方法
* Popen.poll()
用于检查子进程是否已经结束。设置并返回returncode属性。
* Popen.wait()
等待子进程结束。设置并返回returncode属性。
* Popen.communicate(input=None)
与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:`(stdoutdata, stderrdata)`。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
* Popen.send_signal(signal)
向子进程发送信号。
* Popen.terminate()
停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
* Popen.kill()
杀死子进程。
* Popen.stdin
如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。
* Popen.stdout
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。
* Popen.stderr
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。
* Popen.pid
获取子进程的进程ID。
* Popen.returncode
获取进程的返回值。如果进程还没有结束,返回None。
下面是一个非常简单的例子,来演示supprocess模块如何与一个控件台应用程序进行交互。
---
### supprocess其他
* subprocess.PIPE
在创建Popen对象时,subprocess.PIPE可以作为初始化stdin,stdout或stderr参数。表示与子进程通信的标准流。
* subprocess.STDOUT
创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。
* subprocess.call(\*popenargs, \*\*kwargs)
运行命令。该函数将一直等待到子进程运行结束,并返回进程的`returncode`。如果子进程不需要进行交互,就可以使用该函数来创建。
> 报错是shell错误,无法捕捉
* subprocess.check_call(\*popenargs, \*\*kwargs)
与subprocess.call(\*popenargs, \*\*kwargs)功能一样,只是如果子进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包括进程的`returncode`信息。
> 报错是python错误,在python中可以捕捉
### 设置超时时间
通过改写,设置脚本执行的超时时间
~~~
#!/usr/bin/env python
# coding=utf-8
import shlex
import datetime
import subprocess
import time
def execute_command(cmdstring, cwd=None, timeout=None, shell=False):
"""执行一个SHELL命令封装了subprocess的Popen方法, 支持超时判断,支持读取stdout和stderr
参数:
cwd: 运行命令时更改路径,如果被设定,子进程会直接先更改当前路径到cwd
timeout: 超时时间,秒,支持小数,精度0.1秒
shell: 是否通过shell运行
Returns: return_code
Raises: Exception: 执行超时
"""
if shell:
cmdstring_list = cmdstring
else:
cmdstring_list = shlex.split(cmdstring)
if timeout:
end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
#没有指定标准输出和错误输出的管道,因此会打印到屏幕上;
sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE,shell=shell,bufsize=4096)
#subprocess.poll()方法:检查子进程是否结束了,如果结束了,设定并返回码,放在subprocess.returncode变量中
while sub.poll() is None:
time.sleep(0.1)
if timeout:
if end_time <= datetime.datetime.now():
raise Exception("Timeout:%s"%cmdstring)
return str(sub.returncode)
if __name__=="__main__":
print execute_command("sleep 5", timeout=2)
~~~
---
### Popen的wait方法和communicate方法比较
Popen的wait方法之后程序一直没有返回,原因是wait是有可能产生死锁。
使用 subprocess 模块的 Popen 调用外部程序,如果 stdout 或 stderr 参数是 PIPE,并且程序输出超过操作系统的 pipe size时,如果使用 Popen.wait() 方式等待程序结束获取返回值,会导致死锁,程序卡在 wait() 调用上。
`ulimit -a` 看到的 pipe size 是 4KB,那只是每页的大小,查询得知 Linux 默认的 pipe size 是 `64KB`。
#### 对wait方法和communicate方法进行测试
```
#!/usr/bin/env python
# coding: utf-8
import subprocess
def test(size):
print 'start'
cmd = 'dd if=/dev/urandom bs=1 count=%d 2>/dev/null' % size
p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
p.communicate()
#p.wait()
print 'end'
```
测试结果
p.wait工作时,64kb时工作正常,64k+1时,程序卡住,产生死锁。
p.communicate工作时,两种情况下都可以正常工作。
```
# 64KB
test(64 * 1024)
# 64KB + 1B
test(64 * 1024 + 1)
```
官方文档里推荐使用 `Popen.communicate()`。这个方法会把输出放在`内存`,而不是管道里,所以这时候上限就和内存大小有关了。而且如果要获得程序返回值,可以在调用 Popen.communicate() 之后取 Popen.returncode 的值。
结论
如果使用 subprocess.Popen,应该使用 Popen.communicate() 来等待外部程序执行结束。
---
### subprocess的两种方法 阻塞和非阻塞
1)如果想调用之后直接阻塞到子程序调用结束
```
#start and block until done
import shlex, subprocess
cmd = 'dd if=/dev/zero of=bigfile bs=1M count=32'
cmd = shlex.split(cmd)
subprocess.call(cmd)
```
2)非阻塞的时候方式
```
#start and process things, then wait
from subprocess import Popen,PIPE
cmd = 'dd if=/dev/zero of=bigfile bs=1M count=32'
p = Popen(args=cmd, shell=True, stdout=PIPE,stderr=PIPE)
print p.communicate()
print 'end...'
```
- 前言
- 环境搭建
- pypi
- 打包
- Python 2 和 Python 3 的版本之间差别
- 项目
- 第一部分
- 第1章 基础
- Python安装
- python代码文件类型
- python对象
- 核心数据类型
- 核心数据类型--整型和浮点型
- 核心数据类型--字符串
- str.format
- 核心数据类型--列表
- 核心数据类型--元组
- 核心数据类型--字典
- 核心数据类型--集合
- 核心数据类型--文件对象
- 调用bash
- 标准输入输出
- str-repr
- 字符编码
- 迭代器和生成器
- 第2章 语句和语法
- 赋值语句
- if语句
- while语句
- for语句
- assert
- 第3章 函数
- 函数作用域
- 工厂函数
- 内置函数
- 递归
- 嵌套作用域和lambda
- 参数传递
- 函数式编程
- property可写与可读
- 第5章 模块
- 模块导入
- 模块命名空间
- 相对导入和绝对导入
- 模块重载
- 在模块中隐藏数据
- 过渡性重载
- 第6章 类
- 面向对象还是面向过程?
- 构造函数 析构函数
- call
- 运算符重载
- str()
- 待定
- 即时生成属性
- 多态
- 线程和进程
- thread模块
- threading模块
- threading线程锁
- 糖果机
- multiprocessing
- 阻塞非阻塞同步异步
- 单线程和多线程对比
- 生产者消费者模型
- 第二部分
- 获取系统资源信息
- 获取进程所占的物理内存
- dmidecode获取系统信息
- 网络编程
- 网络基础
- python中的套接字
- socket模块
- 第三部分 高级功能
- 闭包入门
- 闭包的应用
- 装饰器入门
- 装饰器应用
- 第四部分 项目实战
- graphite
- 模块
- collections
- datetime
- Enum
- faker
- fabric
- fileinput
- fire
- fnmatch
- getpass
- glob
- hashlib
- heapq
- json模块
- log
- os
- Paramiko
- parser
- platform
- pyyaml
- Queue
- random
- re
- 特殊符号和字符
- re模块
- shelves
- subprocess
- time
- urllib_urllib2_requests
- urllib urllib2
- requests
- 标准模块ConfigParser
- 扩展模块Mysqldb
- 扩展模块dns
- 扩展模块request
- uuid
- cacheout 缓存库
- delorean 时间
- 附录
- 内置函数
- python实现各种排序算法
- 常见报错
- pymongo
- pyrocksdb
- 常用
- ERROR