python中的subprocess.Popen()使用

从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。
subprocess意在替代其他几个老的模块或者函数,比如:os.system os.spawn* os.popen* popen2.* commands.*

一、subprocess.Popen
subprocess模块定义了一个类: Popen
class subprocess.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)

各参数含义如下:

args: 
args参数。可以是一个字符串,可以是一个包含程序参数的列表。要执行的程序一般就是这个列表的第一项,或者是字符串本身。
subprocess.Popen(["cat","test.txt"])
subprocess.Popen("cat test.txt")
这两个之中,后者将不会工作。因为如果是一个字符串的话,必须是程序的路径才可以。(考虑unix的api函数exec,接受的是字符串
列表)
但是下面的可以工作
subprocess.Popen("cat test.txt", shell=True)
这是因为它相当于
subprocess.Popen(["/bin/sh", "-c", "cat test.txt"])
在*nix下,当shell=False(默认)时,Popen使用os.execvp()来执行子程序。args一般要是一个【列表】。如果args是个字符串的
话,会被当做是可执行文件的路径,这样就不能传入任何参数了。

注意:
shlex.split()可以被用于序列化复杂的命令参数,比如:
>>> shlex.split('ls ps top grep pkill')
['ls', 'ps', 'top', 'grep', 'pkill']
>>>import shlex, subprocess
>>>command_line = raw_input()
/bin/cat -input test.txt -output "diege.txt" -cmd "echo '$MONEY'" 
>>>args = shlex.split(command_line)
>>> print args
['/bin/cat', '-input', 'test.txt', '-output', 'diege.txt', '-cmd', "echo '$MONEY'"]
>>>p=subprocess.Popen(args)
可以看到,空格分隔的选项(如-input)和参数(如test.txt)会被分割为列表里独立的项,但引号里的或者转义过的空格不在此列
。这也有点像大多数shell的行为。

在*nix下,当shell=True时,如果arg是个字符串,就使用shell来解释执行这个字符串。如果args是个列表,则第一项被视为命令,
其余的都视为是给shell本身的参数。也就是说,等效于:
subprocess.Popen(['/bin/sh', '-c', args[0], args[1], ...])

在Windows下,下面的却又是可以工作的
subprocess.Popen(["notepad.exe", "test.txt"])
subprocess.Popen("notepad.exe test.txt")
这是由于windows下的api函数CreateProcess接受的是一个字符串。即使是列表形式的参数,也需要先合并成字符串再传递给api函数
subprocess.Popen("notepad.exe test.txt" shell=True)
等同于
subprocess.Popen("cmd.exe /C "+"notepad.exe test.txt" shell=True)

bufsize参数:
如果指定了bufsize参数作用就和内建函数open()一样:0表示不缓冲,1表示行缓冲,其他正数表示近似的缓冲区字节数,负数表
示使用系统默认值。默认是0。

executable参数:
指定要执行的程序。它很少会被用到:一般程序可以由args 参数指定。如果shell=True ,executable 
可以用于指定用哪个shell来执行(比如bash、csh、zsh等)。*nix下,默认是 /bin/sh ,windows下,就是环境变量 COMSPEC 
的值。windows下,只有当你要执行的命令确实是shell内建命令(比如dir ,copy 等)时,你才需要指定shell=True 
,而当你要执行一个基于命令行的批处理脚本的时候,不需要指定此项。

stdin stdout和stderr:
stdin stdout和stderr,分别表示子程序的标准输入、标准输出和标准错误。可选的值有PIPE或者一个有效的文件描述符(其实是个正
整数)或者一个文件对象,还有None。如果是PIPE,则表示需要创建一个新的管道,如果是None
,不会做任何重定向工作,子进程的文件描述符会继承父进程的。另外,stderr的值还可以是STDOUT
,表示子进程的标准错误也输出到标准输出。

preexec_fn参数:
如果把preexec_fn设置为一个可调用的对象(比如函数),就会在子进程被执行前被调用。(仅限*nix)

close_fds参数:
如果把close_fds设置成True,*nix下会在开子进程前把除了0、1、2以外的文件描述符都先关闭。在 Windows下也不会继承其他文件描述符。

shell参数:
如果把shell设置成True,指定的命令会在shell里解释执行。

cwd参数:
如果cwd不是None,则会把cwd做为子程序的当前目录。注意,并不会把该目录做为可执行文件的搜索目录,所以不要把程序文件所在
目录设置为cwd 。

env参数:
如果env不是None,则子程序的环境变量由env的值来设置,而不是默认那样继承父进程的环境变量。注意,即使你只在env里定义了
某一个环境变量的值,也会阻止子程序得到其
他的父进程的环境变量(也就是说,如果env里只有1项,那么子进程的环境变量就只有1个了)。例如:

>>> subprocess.Popen('env', env={'test':'123', 'testtext':'zzz'})
test=123
<subprocess.Popen object at 0x2870ad2c>
testtext=zzz

universal_newlines参数:
如果把universal_newlines 设置成True,则子进程的stdout和stderr被视为文本对象,并且不管是*nix的行结束符('/n' 
),还是老mac格式的行结束符('/r' ),还是windows 格式的行结束符('/r/n' )都将被视为 '/n' 。

startupinfo和creationflags参数:
如果指定了startupinfo和creationflags,将会被传递给后面的CreateProcess()函数,用于指定子程序的各种其他属性,比如主窗口样式或者是
子进程的优先级等。(仅限Windows)

二、subprocess.PIPE
subprocess.PIPE
一个可以被用于Popen的stdin 、stdout 和stderr 3个参数的特输值,表示需要创建一个新的管道。
subprocess.STDOUT
一个可以被用于Popen的stderr参数的输出值,表示子程序的标准错误汇合到标准输出。
实例:
>>>p=subprocess.Popen("df -h",shell=True,stdout=subprocess.PIPE)
>>>out=p.stdout.readlines()
>>>out
[b'Filesystem     Size    Used   Avail Capacity  Mounted on\n', b'/dev/ad0s1a    713M    313M    343M    48%    /\n', b'devfs          1.0K    1.0K      0B   100%    /dev\n', b'/dev/ad0s1e    514M    2.1M    471M     0%    /tmp\n', b'/dev/ad0s1f    4.3G    2.5G    1.4G    64%    /usr\n', b'/dev/ad0s1d    2.0G    121M    1.7G     6%    /var\n'
>>> for line in out:
...     print line.strip()
... 
Filesystem     Size    Used   Avail Capacity  Mounted on
/dev/ad0s1a    713M    313M    343M    48%    /
devfs          1.0K    1.0K      0B   100%    /dev
/dev/ad0s1e    514M    2.1M    471M     0%    /tmp
/dev/ad0s1f    4.3G    2.5G    1.4G    64%    /usr
/dev/ad0s1d    2.0G    121M    1.7G     6%    /var
stdout可以使用read(),readline(),readlines()等方法

三、方便的函数
1、subprocess.call
subprocess.call (*popenargs , **kwargs )
执行命令,并等待命令结束,再返回子进程的返回值。参数同Popen,查看/usr/lib/python2.7/subprocess.py 
去掉文档,其实是这样的:
def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()
>>> subprocess.call('ifconfig',shell=True)

2、subprocess.check_call
subprocess.check_call (*popenargs , **kwargs )
执行上面的call命令,并检查返回值,如果子进程返回非0,则会抛出CalledProcessError异常,这个异常会有个returncode 
属性,记录子进程的返回值。
def check_call(*popenargs, **kwargs):
    retcode = call(*popenargs, **kwargs)
    if retcode:
        cmd = kwargs.get("args")
        raise CalledProcessError(retcode, cmd)
    return 0
>>> subprocess.check_call('ifconfig')  
>>> subprocess.call('noifconfig')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/subprocess.py", line 493, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/local/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/local/lib/python2.7/subprocess.py", line 1228, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
异常子进程里抛出的异常,会在父进程中再次抛出。并且,异常会有个叫child_traceback的额外属性,这是个包含子进程错误traceback
信息的字符串。遇到最多的错误回是 OSError,比如执行了一个并不存在的子程序就会产生OSError。另外,如果使用错误的参数调用Popen
,会抛出ValueError。当子程序返回非0时,check_call()还会产生CalledProcessError 异常。
安全性
不像其他的popen函数,本函数不会调用/bin/sh来解释命令,也就是说,命令中的每一个字符都会被安全地传递到子进程里。

3、check_output 
check_output()执行程序,并返回其标准输出.
def check_output(*popenargs, **kwargs):
    process = Popen(*popenargs, stdout=PIPE, **kwargs)
    output, unused_err = process.communicate()
    retcode = process.poll()
    if retcode:
        cmd = kwargs.get("args")
        raise CalledProcessError(retcode, cmd, output=output)
    return output
p=subprocess.check_output('ifconfig')
结果是所有行/n分割的一个字符串
可以直接print出来 
这里开始

4、Popen对象

产生对象
p=subprocess.Popen("df -h",shell=True,stdout=subprocess.PIPE)
>>> dir(p)

Popen对象有以下方法:

Popen.poll()
检查子进程是否已结束,设置并返回returncode属性。

>>> p.poll()
0

Popen.wait()
等待子进程结束,设置并返回returncode属性。
>>> p.wait() 
0
注意: 如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统pipe的缓存大小的话,
子进程会等待父进程读取管道,而父进程此时正wait着的话,将会产生传说中的死锁,后果是非常严重滴。建议使用
communicate() 来避免这种情况的发生。

Popen.communicate(input=None)
和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。可选的input如有有的话,要为字符串类型。
此函数返回一个元组: (stdoutdata , stderrdata ) 。
注意,要给子进程的stdin发送数据,则Popen的时候,stdin要为PIPE;同理,要可以接收数据的话,stdout或者stderr也要为PIPE。
p1=subprocess.Popen('cat /etc/passwd',shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)              
>>> p2=subprocess.Popen('grep 0:0',shell=True,stdin=p1.stdout,stdout=subprocess.PIPE)
注意:读到的数据会被缓存在内存里,所以数据量非常大的时候要小心了。
>>> p.communicate()     
(b'Filesystem     Size    Used   Avail Capacity  Mounted on\n/dev/ad0s1a    713M    313M    343M    48%    /\ndevfs          1.0K    1.0K      0B   100%    /dev\n/dev/ad0s1e    514M    2.1M    471M     0%    /tmp\n/dev/ad0s1f    4.3G    2.5G    1.4G    64%    /usr\n/dev/ad0s1d    2.0G    121M    1.7G     6%    /var\n', None)

Popen.send_signal(signal)
给子进程发送signal信号。
注意:windows下目前只支持发送SIGTERM,等效于下面的terminate() 。

Popen.terminate()
停止子进程。Posix下是发送SIGTERM信号。windows下是调用TerminateProcess()这个API。

Popen.kill()
杀死子进程。Posix下是发送SIGKILL信号。windows下和terminate() 无异。

Popen.stdin
如果stdin 参数是PIPE,此属性就是一个文件对象,否则为None 。

Popen.stdout
如果stdout参数是PIPE,此属性就是一个文件对象,否则为None 。

Popen.stderr
如果stderr 参数是PIPE,此属性就是一个文件对象,否则为None 。

Popen.pid
子进程的进程号。注意,如果shell 参数为True,这属性指的是子shell的进程号。
>>> p.pid     
22303

Popen.returncode
子程序的返回值,由poll()或者wait()设置,间接地也由communicate()设置。
如果为None,表示子进程还没终止。
如果为负数-N的话,表示子进程被N号信号终止。(仅限*nux)

用subprocess来代替其他函数
都可以用subprocess来完成,我们假定是用 “from subprocess import *” 来导入模块的:

代替shell命令:
p=`ls -l`
等效于
p=Popen(['ls','-l'],stdout=PIPE).communicate()[0]

代替shell管道:
p=`dmesg | grep cpu`
等效于
p1=Popen(['dmesg'],stdout=PIPE)
p2=Popen(['grep','cpu'],stdin=p1.stdout,stdout=PIPE)
output = p2.communicate()[0]
output
cpu0: <ACPI CPU> on acpi0\nacpi_throttle0: <ACPI CPU Throttling> on cpu0\n

>>> p1=subprocess.Popen('cat /etc/passwd',shell=True,stdout=subprocess.PIPE)                
>>> p2=subprocess.Popen('grep 0:0',shell=True,stdin=p1.stdout,stdout=subprocess.PIPE)
>>> p3=subprocess.Popen("cut -d ':' -f 7",shell=True,stdin=p2.stdout,stdout=subprocess.PIPE)
>>> print p3.stdout.read()

代替os.system()
lsl = os.system('ls '+'-l')
这个是一个返回状态
等效于
p=Popen('ls -l', shell=True)
lsl=os.waitpid(p.pid,0)[1]

注意:
通常并不需要用shell来调用程序。用subprocess可以更方便地得到子程序的返回值。
其实,更真实的替换是:
try:
    retcode = call(“mycmd” + ” myarg”, shell=True)
if retcode < 0:
    print >>sys.stderr, “Child was terminated by signal”, -retcode
else:
    print >>sys.stderr, “Child returned”, retcode
    except OSError, e:
    print >>sys.stderr, “Execution failed:”, e

代替os.spawn系列
P_NOWAIT的例子
pid = os.spawnlp(os.P_NOWAIT, “/bin/mycmd”, “mycmd”, “myarg”)
等效于
pid = Popen(["/bin/mycmd", "myarg"]).pid

P_WAIT的例子

retcode = os.spawnlp(os.P_WAIT, “/bin/mycmd”, “mycmd”, “myarg”)
等效于
retcode = call(["/bin/mycmd", "myarg"])

返回值处理:

pipe = os.popen(“cmd”, ‘w’)
...
rc = pipe.close()
if rc != None and rc % 256:
    print “There were some errors”
等效于
process = Popen(“cmd”, ‘w’, shell=True, stdin=PIPE)
...
process.stdin.close()
if process.wait() != 0:
    print “There were some errors”

python中的subprocess.Popen()使用的更多相关文章

  1. python中的subprocess.Popen()使用详解---以及注意的问题(死锁)

    从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值. subprocess意在替代其他几个老的模块或者函数 ...

  2. Python中的subprocess模块

    Subprocess干嘛用的? subprocess模块是python从2.4版本开始引入的模块.主要用来取代 一些旧的模块方法,如os.system.os.spawn*.os.popen*.comm ...

  3. Python中使用subprocess模块远程执行命令

    使用subprocess模块执行远程命令 服务端代码 1 import socket 2 import subprocess 3 4 sh_server = socket.socket() 5 sh_ ...

  4. python中常用的知识

    python中一切事务皆为对象. 所以我们看字符串.数字.集合等全部使用类的方法查看某一个对象. a = 'sb,2b' 查看对象是什么类型:print(type(a)) 查看此对象有哪些属性:dir ...

  5. python子进程模块subprocess详解与应用实例 之三

    二.应用实例解析 2.1 subprocess模块的使用 1. subprocess.call >>> subprocess.call(["ls", " ...

  6. python中subprocess.Popen执行命令并持续获取返回值

    先举一个Android查询连接设备的命令来看看Python中subprocess.Popen怎么样的写法.用到的命令为 adb devices. import subprocess order='ad ...

  7. python中subprocess.Popen.poll

    import subprocess proc = subprocess.Popen(['python', 'test.py'], stdout=subprocess.PIPE) while 1: pr ...

  8. Python subprocess.Popen中communicate()和wait()区别

    刚开始我是使用的wait(),但是当adb命令返回太多时,程序就会卡死,查询得知原因后,才使用了communicate(),communicate()返回一个元组:(stdoutdata, stder ...

  9. python中subprocess.Popen的args和shell参数的使用

    subprocess模块定义了一个类: Popen class subprocess.Popen( args,        bufsize=0,        executable=None,    ...

随机推荐

  1. SQL2012导出的脚本没有if exists判断

    SQL2012导出的脚本没有if exists判断 以前用SQL2000的时候,导出存储过程,表各种object的时候,前面会自动加if exists的一段脚本,这样的脚本很方便的可以重复执行.现在使 ...

  2. ASP.Net Controls 用法大全

    The FindControl method of the System.Web.UI.Control class appears simple enough to use. In fact, the ...

  3. JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)

    一.面向对象编程(继承) 这篇博客是面向对象编程的第三篇,JS继承.继承顾名思义,就是获取父辈的各种"财产"(属性和方法). 怎么实现继承? 我们的JavaScript比较特别了, ...

  4. Redis全面介绍

    最近重新认识了一下Redis,借着这个机会,也整理一篇算是比较详尽和全面的文章吧.   缓存 缓存就是数据交换的缓冲区(称作Cache)——摘自百度百科.无论是在计算机硬件体系结构还是软件体系结构中, ...

  5. 最基本的mysql

    SQL 连接数据库 mysql -u root -p 备份数据库 mysql -u root -p 要备份数据库名>文件位置和文件名 恢复数据库 方式1 mysql -u root -p DBN ...

  6. PHP操作MySQL数据库之天龙八部 -- 七贱下天山 -- 六脉神剑

    天龙八部            八步操作数据库 七贱下天山        七步操作数据库  (将判断错误省略) 六脉神剑            六步操作数据库(将判断错误省略,将选择数据库添加到第一步 ...

  7. 基于vue,打印机打印暂且处理

    基于vue单页面应用.暂且没找到合适的方案,什么vue-print  .jquery.print.js.jqprint.js..canvas生成图片啊 大多不能保证页面样式保持原样. 所以,选择了最土 ...

  8. MATLAB 大数据剔除坏值

    在用MATLAB进行数据分析的时候,坏点对正确结果的影响比较大, 因此,我么需要剔除野点,对于坏值的剔除,我们 利用  3σ准则 剔除无效数据: 3σ准则又称为拉依达准则,它是先假设一组检测数据只含有 ...

  9. python3学习笔记(1)

    一.模块初识(接上篇)模块(库)分为两种:1.标准库:不需要安装直接可以导入的库,例:getpass.2.第三方库:必须要下载安装才可以使用.注:编辑的模块文件名不能与导入的模块名重复. 在模块文件中 ...

  10. Less的条件表达式

    Less的条件表达式 当需要根据表达式,而不是参数的值或数量进行匹配时,条件表达式(Guards)就显得非常有用.如果你熟悉函数式编程的话,对条件表达式也不会陌生. 为了尽可能地接近CSS的语言结构, ...