• 【转】Python模块subprocess


    subprocess

    早期的Python版本中,我们主要是通过os.system()、os.popen().read()等函数、commands模块来执行命令行指令的,从Python 2.4开始官方文档新引入了一个模块subprocess,subprocess替换os.system等,允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。 

    import commands
    result = commands.getoutput('cmd')
    result = commands.getstatus('cmd')
    result = commands.getstatusoutput('cmd')

    subprocess的目的就是启动一个新的进程并且与之通信。

    1. subprocess模块中的常用函数

    函数描述
    subprocess.run() Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
    subprocess.call()

    父进程等待子进程执行命令,返回子进程执行命令的状态码,如果出现错误,不进行报错,其功能类似于os.system(cmd)。

    res = subprocess.call(['dir',shell=True]) 获取的执行结果,我们能获取到的是子进程执行命令执行结果的状态码,即res=0/1 执行成功或者不成功,并不代表说看不到执行结果,在Python的console界面中我们是能够看到命令结果的,只是获取不到。想获取执行的返回结果,请看check_output。】

    subprocess.check_call() Python 2.5中新增的函数。父进程等待子进程执行命令,返回执行命令的状态码,如果出现错误,进行报错【如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性,可用try…except…来检查】
    subprocess.check_output() Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
    subprocess.getoutput(cmd) 接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
    subprocess.getstatusoutput(cmd) 执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。

    说明:

    1. 在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能;
    2. 在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;
    3. subprocess.run()、subprocess.call()、subprocess.check_call()和subprocess.check_output()都是通过对subprocess.Popen的封装来实现的高级函数,因此如果我们需要更复杂功能时,可以通过subprocess.Popen来完成。
    4. subprocess.getoutput()和subprocess.getstatusoutput()函数是来自Python 2.x的commands模块的两个遗留函数。它们隐式的调用系统shell,并且不保证其他函数所具有的安全性和异常处理的一致性。另外,它们从Python 3.3.4开始才支持Windows平台。

    1. call:

    2. check_call

    返回结果

    复制代码
    2. ################## subprocess.check_call ##########
    check_call与call命令相同,区别是如果出错会报错
     驱动器 D 中的卷没有标签。
     卷的序列号是 C6A1-5AD3
    
     D:ProgramPython 的目录
    
    2016/01/27  13:05    <DIR>          .
    2016/01/27  13:05    <DIR>          ..
    2016/01/27  10:44    <DIR>          .idea
    2016/01/27  11:23               159 log_analyse.py
    2016/01/27  13:05             1,329 subprocessDemo.py
                   2 个文件          1,488 字节
                   3 个目录 26,335,281,152 可用字节
    'abc' 不是内部或外部命令,也不是可运行的程序或批处理文件。                    这里是系统执行命令返回的系统报错
    Traceback (most recent call last):                              这里是Python解释器返回的报错
      File "D:/Program/Python/subprocessDemo.py", line 19, in <module>
        subprocess.check_call(['abc'],shell=True)
      File "C:Python27libsubprocess.py", line 540, in check_call
        raise CalledProcessError(retcode, cmd)
    subprocess.CalledProcessError: Command '['abc']' returned non-zero exit status 1
    复制代码

    3. check_output

    父进程等待子进程执行命令,返回子进程向标准输出发送输出运行结果,检查退出信息,如果returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。

    复制代码
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import subprocess
    
    print "3. ################## subprocess.check_output ##############"
    res1 = subprocess.call(['dir'],shell=True)
    res2 = subprocess.check_call(['dir'],shell=True)
    res3 = subprocess.check_output(['dir'],shell=True)
    print u"call结果:",res1
    print u"check_call结果:",res2
    print u"check_output结果:
    ",res3
    复制代码

    返回结果:

    复制代码
    3. ################## subprocess.output ##############
     驱动器 D 中的卷没有标签。
     卷的序列号是 C6A1-5AD3
    
     D:ProgramPython 的目录
    
    2016/01/27  13:14    <DIR>          .
    2016/01/27  13:14    <DIR>          ..
    2016/01/27  10:44    <DIR>          .idea
    2016/01/27  11:23               159 log_analyse.py
    2016/01/27  13:14             1,324 subprocessDemo.py
                   2 个文件          1,483 字节
                   3 个目录 26,334,232,576 可用字节
     驱动器 D 中的卷没有标签。
     卷的序列号是 C6A1-5AD3
    
     D:ProgramPython 的目录
    
    2016/01/27  13:14    <DIR>          .
    2016/01/27  13:14    <DIR>          ..
    2016/01/27  10:44    <DIR>          .idea
    2016/01/27  11:23               159 log_analyse.py
    2016/01/27  13:14             1,324 subprocessDemo.py
                   2 个文件          1,483 字节
                   3 个目录 26,334,232,576 可用字节
    call结果: 0
    check_call结果: 0
    check_output结果:
     驱动器 D 中的卷没有标签。
     卷的序列号是 C6A1-5AD3
    
     D:ProgramPython 的目录
    
    2016/01/27  13:14    <DIR>          .
    2016/01/27  13:14    <DIR>          ..
    2016/01/27  10:44    <DIR>          .idea
    2016/01/27  11:23               159 log_analyse.py
    2016/01/27  13:14             1,324 subprocessDemo.py
                   2 个文件          1,483 字节
                   3 个目录 26,334,232,576 可用字节
    复制代码

    可见,call/check_call  返回值均是命令的执行状态码,而check_output返回值是命令的执行结果。

    如果在执行相关命令时,命令后带有参数,将程序名(即命令)和所带的参数一起放在一个列表中传递给相关犯法即可,例如:

    >>> import subprocess
    >>> retcode = subprocess.call(["ls", "-l"])
    >>> print retcode
    0

    4. Popen

      实际上,subprocess模块中只定义了一个类: Popen。上面的几个函数都是基于Popen()的封装(wrapper)。从Python2.4开始使用Popen来创建进程,用于连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。

    构造函数如下:

    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)

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

    a) 不等待的子进程

    #!/usr/bin/env python
    import subprocess
    
    child = subprocess.Popen(['ping','-c','4','www.baidu.com'])
    print 'hello'

    执行结果:

    复制代码
    [root@localhost script]# python sub.py 
    hello
    [root@localhost script]# PING www.a.shifen.com (61.135.169.125) 56(84) bytes of data.
    64 bytes from 61.135.169.125: icmp_seq=1 ttl=55 time=2.04 ms
    64 bytes from 61.135.169.125: icmp_seq=2 ttl=55 time=1.58 ms
    64 bytes from 61.135.169.125: icmp_seq=3 ttl=55 time=2.22 ms
    64 bytes from 61.135.169.125: icmp_seq=4 ttl=55 time=2.13 ms
    
    --- www.a.shifen.com ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 3008ms
    rtt min/avg/max/mdev = 1.580/1.995/2.220/0.251 ms
    复制代码

    可以看出,Python并没有等到child子进程执行的Popen操作完成就执行了print操作。

    b) 添加子进程等待

    #!/usr/bin/env python
    import subprocess
    
    child = subprocess.Popen(['ping','-c','4','www.baidu.com'])  #创建一个子进程,进程名为child,执行操作ping -c 4 www.baidu.com
    child.wait()                             #子进程等待
    print 'hello'

    执行结果:

    复制代码
    [root@localhost script]# python sub.py 
    PING www.a.shifen.com (61.135.169.125) 56(84) bytes of data.
    64 bytes from 61.135.169.125: icmp_seq=1 ttl=55 time=1.82 ms
    64 bytes from 61.135.169.125: icmp_seq=2 ttl=55 time=1.65 ms
    64 bytes from 61.135.169.125: icmp_seq=3 ttl=55 time=1.99 ms
    64 bytes from 61.135.169.125: icmp_seq=4 ttl=55 time=2.08 ms
    
    --- www.a.shifen.com ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 3009ms
    rtt min/avg/max/mdev = 1.656/1.889/2.082/0.169 ms
    hello
    复制代码

    看出Python执行print操作是在child子进程操作完成以后才进行的。

    此外,你还可以在父进程中对子进程进行其它操作,比如我们上面例子中的child对象:

    child.poll() # 检查子进程状态
    child.kill() # 终止子进程
    child.send_signal() # 向子进程发送信号
    child.terminate() # 终止子进程
    ps: 子进程的PID存储在child.pid

    2. subprocess.Popen类的实例可调用的方法

    方法描述
    Popen.poll() 用于检查子进程(命令)是否已经执行结束,没结束返回None,结束后返回状态码。
    Popen.wait(timeout=None) 等待子进程结束,并返回状态码;如果在timeout指定的秒数之后进程还没有结束,将会抛出一个TimeoutExpired异常。
    Popen.communicate(input=None, timeout=None) 该方法可用来与进程进行交互,比如发送数据到stdin,从stdout和stderr读取数据,直到到达文件末尾。
    Popen.send_signal(signal) 发送指定的信号给这个子进程。
    Popen.terminate() 停止该子进程。
    Popen.kill() 杀死该子进程。

    关于communicate()方法的说明:

    • 该方法中的可选参数 input 应该是将被发送给子进程的数据,或者如没有数据发送给子进程,该参数应该是None。input参数的数据类型必须是字节串,如果universal_newlines参数值为True,则input参数的数据类型必须是字符串。
    • 该方法返回一个元组(stdout_data, stderr_data),这些数据将会是字节穿或字符串(如果universal_newlines的值为True)。
    • 如果在timeout指定的秒数后该进程还没有结束,将会抛出一个TimeoutExpired异常。捕获这个异常,然后重新尝试通信不会丢失任何输出的数据。但是超时之后子进程并没有被杀死,为了合理的清除相应的内容,一个好的应用应该手动杀死这个子进程来结束通信。
    • 需要注意的是,这里读取的数据是缓冲在内存中的,所以,如果数据大小非常大或者是无限的,就不应该使用这个方法。

    3. subprocess.Popen使用实例

    实例1:

    >>> import subprocess
    >>>
    >>> p = subprocess.Popen('df -Th', stdout=subprocess.PIPE, shell=True)
    >>> print(p.stdout.read())
    Filesystem     Type      Size  Used Avail Use% Mounted on
    /dev/vda1      ext4       40G   12G   26G  31% /
    devtmpfs       devtmpfs  3.9G     0  3.9G   0% /dev
    tmpfs          tmpfs     3.9G     0  3.9G   0% /dev/shm
    tmpfs          tmpfs     3.9G  386M  3.5G  10% /run
    tmpfs          tmpfs     3.9G     0  3.9G   0% /sys/fs/cgroup
    tmpfs          tmpfs     783M     0  783M   0% /run/user/0
    tmpfs          tmpfs     783M     0  783M   0% /run/user/1000

    实例2:

    >>> obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    >>> obj.stdin.write('print(1) 
    ')
    >>> obj.stdin.write('print(2) 
    ')
    >>> obj.stdin.write('print(3) 
    ')
    >>> out,err = obj.communicate()
    >>> print(out)
    1
    2
    3
    
    >>> print(err)
    

    实例3:

    >>> obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    >>> out,err = obj.communicate(input='print(1) 
    ')
    >>> print(out)
    1
    
    >>> print(err)
    

    实例4:

    实现类似df -Th | grep data命令的功能,实际上就是实现shell中管道的共功能。

    >>> 
    >>> p1 = subprocess.Popen(['df', '-Th'], stdout=subprocess.PIPE)
    >>> p2 = subprocess.Popen(['grep', 'data'], stdin=p1.stdout, stdout=subprocess.PIPE)
    >>> out,err = p2.communicate()
    >>> print(out)
    /dev/vdb1      ext4      493G  4.8G  463G   2% /data
    /dev/vdd1      ext4     1008G  420G  537G  44% /data1
    /dev/vde1      ext4      985G  503G  432G  54% /data2
    
    >>> print(err)
    None

    四、总结


    那么我们到底该用哪个模块、哪个函数来执行命令与系统及系统进行交互呢?下面我们来做个总结:

    • 首先应该知道的是,Python2.4版本引入了subprocess模块用来替换os.system()、os.popen()、os.spawn*()等函数以及commands模块;也就是说如果你使用的是Python 2.4及以上的版本就应该使用subprocess模块了。
    • 如果你的应用使用的Python 2.4以上,但是是Python 3.5以下的版本,Python官方给出的建议是使用subprocess.call()函数。Python 2.5中新增了一个subprocess.check_call()函数,Python 2.7中新增了一个subprocess.check_output()函数,这两个函数也可以按照需求进行使用。
    • 如果你的应用使用的是Python 3.5及以上的版本(目前应该还很少),Python官方给出的建议是尽量使用subprocess.run()函数。
    • 当subprocess.call()、subprocess.check_call()、subprocess.check_output()和subprocess.run()这些高级函数无法满足需求时,我们可以使用subprocess.Popen类来实现我们需要的复杂功能。

     

  • 相关阅读:
    WINCE6.0 error C2220: warning treated as error问题解决
    GPRS连接失败问题
    wince下sourcessources.cmnMakefile.def的相关作用
    IMX515开发备忘
    WINCE补丁包下载地址
    IMX51+WINCE6.0平台缩写意义
    喵哈哈村的魔法考试 Round #1 (Div.2) ABCD
    Codeforces Round #402 (Div. 2) D
    Codeforces Round #402 (Div. 2) C
    Codeforces Round #402 (Div. 2) B
  • 原文地址:https://www.cnblogs.com/yoyo008/p/9487551.html
Copyright © 2020-2023  润新知