zoukankan      html  css  js  c++  java
  • subprocess

    Date: 2019-05-28

    Author: Sun

    运行一个进程

    ​ 运行python的时候,我们都是在创建并运行一个进程。像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序。在Python中,我们通过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。

    subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。

    1. subprocess.call()

    函数格式如下:

    call(*popenargs, timeout=None, **kwargs):
        """Run command with arguments.  Wait for command to complete or
        timeout, then return the returncode attribute.
    
        The arguments are the same as for the Popen constructor.  Example:
    
        retcode = call(["ls", "-l"])
    

    父进程等待子进程完成
    返回退出信息(returncode,相当于Linux exit code)

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

    或者是

    >>> a = subprocess.call(['df','-hT'],shell=False)
    Filesystem    Type    Size  Used Avail Use% Mounted on
    /dev/sda2     ext4     94G   64G   26G  72% /
    tmpfs        tmpfs    2.8G     0  2.8G   0% /dev/shm
    /dev/sda1     ext4    976M   56M  853M   7% /boot
    

    subprocess.check_call():用法与subprocess.call()类似,区别是,当返回值不为0时,直接抛出异常

    >>> a = subprocess.check_call('df -hT',shell=True)
    Filesystem    Type    Size  Used Avail Use% Mounted on
    /dev/sda2     ext4     94G   64G   26G  72% /
    tmpfs        tmpfs    2.8G     0  2.8G   0% /dev/shm
    /dev/sda1     ext4    976M   56M  853M   7% /boot
    >>> print a
    0
    >>> a = subprocess.check_call('dfdsf',shell=True)
    /bin/sh: dfdsf: command not found
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib64/python2.6/subprocess.py", line 502, in check_call
        raise CalledProcessError(retcode, cmd)
    subprocess.CalledProcessError: Command 'dfdsf' returned non-zero exit status 127
    

    2. subprocess.Popen()

    ​ 在一些复杂场景中,我们需要将一个进程的执行输出作为另一个进程的输入。在另一些场景中,我们需要先进入到某个输入环境,然后再执行一系列的指令等。这个时候我们就需要使用到suprocess的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对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)

    import subprocess
    
    
    if __name__ == "__main__":
        child = subprocess.Popen('ping -c  www.baidu.com', shell=True)
        child.wait()
        print('parent process')
    

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

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

    子进程的标准输入、标准输出和标准错误, 如下属性分别表示:
    child.stdin
    child.stdout
    child.stderr

    示例,将一个子进程的输出,作为另一个子进程的输入:

    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()
    

    案例分析:

    在工作中经常会遇到这样的需求:

    需要采用python来运行一个shell脚本,如何优雅的操作呢?

    解决方案:

    ​ 用python的subprocess去执行传递过来的脚本,通常情况下subprocess都能运行的很好,完成脚本的执行并返回。

    可以采用如下代码实现:

    # -*- coding: utf-8 -*-
    __author__ = 'sun'
    __date__ = '2019/5/28 18:26'
    
    import subprocess
    from threading import Timer
    import os
    import time
    import signal
    
    class TestSubProcess(object):
        def __init__(self):
            self.stdout = []
            self.stderr = []
            self.timeout = 6
            self.is_timeout = False
    
        def timeout_callback(self, p):
            print('exe time out call back')
            try:
                p.kill()
                # os.killpg(p.pid, signal.SIGKILL)
            except Exception as error:
                print(error)
    
        def run(self):
            stdout = open('/tmp/subprocess_stdout', 'wb')
            stderr = open('/tmp/subprocess_stderr', 'wb')
            cmd = ['bash', '/home/xxx/while_test.sh']
            ps = subprocess.Popen(cmd, stdout=stdout.fileno(), stderr=stderr.fileno())
            my_timer = Timer(self.timeout, self.timeout_callback, [ps])
            my_timer.start()
            print(ps.pid)
            try:
                print("start to count timeout; timeout set to be %d \n" % (self.timeout,))
                ps.wait()
            finally:
                my_timer.cancel()
                stdout.flush()
                stderr.flush()
                stdout.close()
                stderr.close()
    
    
    if __name__ == "__main__":
        tsp = TestSubProcess()
        tsp.run()
    

    总结:

    ​ 关于p = subprocess.Popen,最好用p.communicate.而不是直接用p.wait(), 因为p.wait()有可能因为子进程往PIPE写的时候写满了,但是子进程还没有结束,导致子进程阻塞,而父进程一直在wait(),导致父进程阻塞。而且p.wait()和p.communicate不能一起用,因为p.communicate里面也会去调用wait()。

  • 相关阅读:
    [打印管理器]读取样式列表失败:Invalid variant operation
    分销产品安装在Win7系统上,日期格式错误的解决方案
    如何查看IIS与MSSQL数据库连接池
    分销研发部博客园正式开通了,欢迎大家关注
    asp.net Application_start()方法无法命中断点
    程序健壮性之“异常处理”
    解决问题--在Excel中使用条码字体打印出的code128条码不能被识别
    程序中的数据精度处理问题
    Spring.NET 学习笔记
    ASP.Net MVC开发基础学习笔记(1):走向MVC模式
  • 原文地址:https://www.cnblogs.com/sunBinary/p/10940885.html
Copyright © 2011-2022 走看看