subprocess

Date: 2019-05-28python

Author: Sunshell

运行一个进程bash

​ 运行python的时候,咱们都是在建立并运行一个进程。像Linux进程那样,一个进程能够fork一个子进程,并让这个子进程exec另一个程序。在Python中,咱们经过标准库中的subprocess包来fork一个子进程,并运行一个外部的程序。函数

subprocess包中定义有数个建立子进程的函数,这些函数分别以不一样的方式建立子进程,因此咱们能够根据须要来从中选取一个使用。code

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)utf-8

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

或者是cmd

>>> 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时,直接抛出异常it

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

相关文章
相关标签/搜索