subprocess 模块

1 简介

  从Python 2.4开始,Python引入subprocess模块来管理子进程,以取代一些旧模块的方法:如 os.system、os.spawn*、os.popen*、popen2.*、commands.*不但可以调用外部的命令作为子进程,而且可以连接到子进程的input/output/error管道,获取相关的返回信息。python3也取消了之前在python中常用来获取运行linux命令行结果的commands.*方法,因此,有必要逐渐熟悉新的方法。

FUNCTIONS
    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"])
    
    check_call(*popenargs, **kwargs)
        Run command with arguments.  Wait for command to complete.  If
        the exit code was zero then return, otherwise raise
        CalledProcessError.  The CalledProcessError object will have the
        return code in the returncode attribute.
        
        The arguments are the same as for the call function.  Example:
        
        check_call(["ls", "-l"])
    
    check_output(*popenargs, timeout=None, **kwargs)
        Run command with arguments and return its output.
        
        If the exit code was non-zero it raises a CalledProcessError.  The
        CalledProcessError object will have the return code in the returncode
        attribute and output in the output attribute.
        
        The arguments are the same as for the Popen constructor.  Example:
        
        >>> check_output(["ls", "-l", "/dev/null"])
        b'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'
        
        The stdout argument is not allowed as it is used internally.
        To capture standard error in the result, use stderr=STDOUT.
        
        >>> check_output(["/bin/sh", "-c",
        ...               "ls -l non_existent_file ; exit 0"],
        ...              stderr=STDOUT)
        b'ls: non_existent_file: No such file or directory\n'
        
        There is an additional optional argument, "input", allowing you to
        pass a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it too will be used internally.  Example:
        
        >>> check_output(["sed", "-e", "s/foo/bar/"],
        ...              input=b"when in the course of fooman events\n")
        b'when in the course of barman events\n'
        
        If universal_newlines=True is passed, the "input" argument must be a
        string and the return value will be a string rather than bytes.
    
    getoutput(cmd)
        Return output (stdout or stderr) of executing cmd in a shell.
        
        Like getstatusoutput(), except the exit status is ignored and the return
        value is a string containing the command's output.  Example:
        
        >>> import subprocess
        >>> subprocess.getoutput('ls /bin/ls')
        '/bin/ls'
    
    getstatusoutput(cmd)
        Return (status, output) of executing cmd in a shell.
        
        Execute the string 'cmd' in a shell with 'check_output' and
        return a 2-tuple (status, output). The locale encoding is used
        to decode the output and process newlines.
        
        A trailing newline is stripped from the output.
        The exit status for the command can be interpreted
        according to the rules for the function 'wait'. Example:
        
        >>> import subprocess
        >>> subprocess.getstatusoutput('ls /bin/ls')
        (0, '/bin/ls')
        >>> subprocess.getstatusoutput('cat /bin/junk')
        (256, 'cat: /bin/junk: No such file or directory')
        >>> subprocess.getstatusoutput('/bin/junk')
        (256, 'sh: /bin/junk: not found')
    
    run(*popenargs, input=None, timeout=None, check=False, **kwargs)
        Run command with arguments and return a CompletedProcess instance.
        
        The returned instance will have attributes args, returncode, stdout and
        stderr. By default, stdout and stderr are not captured, and those attributes
        will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
        
        If check is True and the exit code was non-zero, it raises a
        CalledProcessError. The CalledProcessError object will have the return code
        in the returncode attribute, and output & stderr attributes if those streams
        were captured.
        
        If timeout is given, and the process takes too long, a TimeoutExpired
        exception will be raised.
        
        There is an optional argument "input", allowing you to
        pass a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it will be used internally.
        
        The other arguments are the same as for the Popen constructor.
        
        If universal_newlines=True is passed, the "input" argument must be a
        string and stdout/stderr in the returned object will be strings rather than
        bytes.
subprocess publice function

 

2 常用函数 

subprocess.call()

>>> subprocess.call(["ls","-l"],shell=False)
total 36
-rw-r--r-- 1 root root  238 May  4 11:07 ex01.py
-rw-r--r-- 1 root root  590 May  4 14:14 ex02.py
-rw-r--r-- 1 root root  593 May  4 14:53 ex03.py
-rw-r--r-- 1 root root  668 May  5 10:40 ex04.py
-rw-r--r-- 1 root root  608 May  4 16:31 ex05.py
-rw-r--r-- 1 root root  588 May  5 15:02 ex06.py
-rw-r--r-- 1 root root 1858 May  5 10:41 passwd
-rw-r--r-- 1 root root    0 May  5 15:02 passwd.bak
-rw-r--r-- 1 root root    0 May  5 15:02 passwd.tmp
-rw-r--r-- 1 root root   43 May  5 10:36 samples.bak
-rw-r--r-- 1 root root   37 May  5 10:40 samples.txt
0
>>> subprocess.call(["ls","-l"],shell=True)
ex01.py  ex02.py  ex03.py  ex04.py  ex05.py  ex06.py  passwd  passwd.bak  passwd.tmp  samples.bak  samples.txt
0

**:shell = True /Flase :  当等于False的时候前面的参数必须是序列,等于True的时候,前面的参数必须是字符串。因为 等于True的时候,会把字符串重新置成列表。

subprocess.check_call()

>>> subprocess.check_call(["cat","1.txt","2.txt"])
a
b
c
1
2
3
0

返回结果同call()方法一致,但若returncode不为0,则抛出异常CalledProcessError。可用try...except...来捕获

注:cat多个文件合成一个文件时,文件连接间用空行分隔了

subprocess.check_output()

 result=subprocess.check_output(["cat","1.txt","2.txt"])

返回标准输出的结果,且若returncode不为0,同样抛出CalledProcessError。例如:

>>> subprocess.check_output("exit 1", shell=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/python3/lib/python3.6/subprocess.py", line 336, in check_output
    **kwargs).stdout
  File "/usr/local/python3/lib/python3.6/subprocess.py", line 418, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1.

 

3 底层函数

subprocess.Popen

1)指定stdout = subprocess.PIPE获取标准输出:

>>> res = subprocess.Popen("cat 1.txt 2.txt",shell=True,stdout=subprocess.PIPE)
>>> print(res)
<subprocess.Popen object at 0x7fd1a419e358>
>>> res.communicate()
(b'a\nb\nc\n1\n2\n3\n', None)

运行结果为 标准输出,标准错误输出的元组

注意communicate的作用:

>与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。例如:

>>> p=subprocess.Popen("ls",shell=True,stdout=subprocess.PIPE)
>>> p.communicate("-1")
(b'1.txt\n2.txt\nex01.py\nex02.py\nex03.py\nex04.py\nex05.py\nex06.py\npasswd\npasswd.bak\npasswd.tmp\nsamples.bak\nsamples.txt\n', None)

>Communicate()返回一个元组:(stdoutdata, stderrdata)

>subprocess.PIPE实际提供了一个缓冲区。communicate将数据从缓冲区读出来。

注:如果你设定了stdout = subprocess.PIPE,而子进程非常多时候,容易死锁。

 

捕获输出结果:

>>> output=subprocess.check_output("ls -l",shell=True)
>>> output
b'total 12\n-rw-r--r-- 1 root root    6 May 16 15:14 1.txt\n-rw-r--r-- 1 root root    6 May 16 15:14 2.txt\ndrwxr-xr-x 2 root root 4096 May 16 15:45 bak\n'
>>> print(output.decode('utf-8'))
total 12
-rw-r--r-- 1 root root    6 May 16 15:14 1.txt
-rw-r--r-- 1 root root    6 May 16 15:14 2.txt
drwxr-xr-x 2 root root 4096 May 16 15:45 bak
以下例子将chek_output()方法执行命令异常时的错误捕获,而避免输出到控制台.
>>> try:
...     output = subprocess.check_output("lT -l", shell=True, stderr=subprocess.STDOUT)
... except subprocess.CalledProcessError as err:
...     print("Command Error", err)
... 
Command Error Command 'lT -l' returned non-zero exit status 127.

 

 

直接处理管道

subprocess.Popen()方法:函数call(), check_call() 和 check_output() 都是Popen类的包装器。直接使用Popen会对如何运行命令以及如何处理其输入输出有更多控制。如通过为stdin, stdout和stderr传递不同的参数。

1.与进程的单向通信 

通过Popen()方法调用命令后执行的结果,可以设置stdout值为PIPE,再调用communicate()获取结果 
返回结果为tuple. 在python3中结果为byte类型,要得到str类型需要decode转换一下

直接执行命令输出到屏幕
>>> subprocess.Popen("ls -l",shell=True)
<subprocess.Popen object at 0x7f2436aa39b0>
>>> total 12
-rw-r--r-- 1 root root    6 May 16 15:14 1.txt
-rw-r--r-- 1 root root    6 May 16 15:14 2.txt
drwxr-xr-x 2 root root 4096 May 16 15:45 bak

不输出到屏幕,输出到变量
>>> res = subprocess.Popen(['echo','"Stdout"'],stdout=subprocess.PIPE)
>>> res.communicate()
(b'"Stdout"\n', None)
>>> res = subprocess.Popen(['ls','l'],shell=True,stdout=subprocess.PIPE)
>>> stdout_value = res.communicate()
>>> stdout_value
(b'1.txt\n2.txt\nbak\n', None)
>>> print((stdout_value[0]).decode('utf-8'))
1.txt
2.txt
bak

将结果输出到文件
>>> file_handle = open('./test.log','w+')
>>> subprocess.Popen("ls -l",shell=True,stdout=file_handle)
<subprocess.Popen object at 0x7f24365aada0>
>>> subprocess.Popen("cat test.log",shell=True)
<subprocess.Popen object at 0x7f24365aae80>
>>> total 12
-rw-r--r-- 1 root root    6 May 16 15:14 1.txt
-rw-r--r-- 1 root root    6 May 16 15:14 2.txt
drwxr-xr-x 2 root root 4096 May 16 15:45 bak
-rw-r--r-- 1 root root    0 May 16 15:58 test.log

 

2 与进程的双向通信

>>> import subprocess
>>> res = subprocess.Popen('cat', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> msg = 'Hello world'.encode('utf-8')
#写入到输入管道
>>> res.stdin.write(msg)
11
>>> stdout_value = res.communicate()
>>> stdout_value
(b'Hello world', None)
# 在需要进行相互交互的输入输出过程也可以使用shtin来实现
# 以下实现打开python3的终端,执行一个print命令
>>> proc = subprocess.Popen(['python3'],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE,)
>>> proc.stdin.write('print("helloworld")'.encode('utf-8'))
19
>>> out_value,err_value=proc.communicate()
>>> print(out_value)
b'helloworld\n'
>>> print(err_value)
b''

 

Popen.communicate()方法用于和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。

Popen其它方法

  1. Popen.pid 查看子进程ID
  2. Popen.returncode 获取子进程状态码,0表示子进程结束,None未结束

在使用Popen调用系统命令式,建议使用communicate与stdin进行交互并获取输出(stdout),这样能保证子进程正常退出而避免出现僵尸进程

>>> proc = subprocess.Popen('ls -l', shell=True, stdout=subprocess.PIPE)
# 当前子进程ID
>>> proc.pid
28906
# 返回状态为None,进程未结束
>>> print(proc.returncode)
None
# 通过communicate提交后
>>> out_value = proc.communicate()
>>> proc.pid
28906
# 返回状态为0,子进程自动结束
>>> print(proc.returncode)
0

 

 1 Popen.poll():用于检查子进程是否已经结束。设置并返回returncode属性。
 2 Popen.wait():等待子进程结束。设置并返回returncode属性。
 3 Popen.communicate(input=None):与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
 4 Popen.send_signal(signal):向子进程发送信号。
 5 Popen.terminate():停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
 6 Popen.kill():杀死子进程。
 7 Popen.stdin:如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。
 8 Popen.stdout:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。
 9 Popen.stderr:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。
10 Popen.pid:获取子进程的进程ID。
11 Popen.returncode:获取进程的返回值。如果进程还没有结束,返回None。
12 subprocess.call(*popenargs, **kwargs):运行命令。该函数将一直等待到子进程运行结束,并返回进程的returncode。文章一开始的例子就演示了call函数。如果子进程不需要进行交互,就可以使用该函数来创建。
13 subprocess.check_call(*popenargs, **kwargs):与subprocess.call(*popenargs, **kwargs)功能一样,只是如果子进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包括进程的returncode信息。

 

 1 Popen对象
 2 Popen对象有以下方法:
 3 Popen.poll()
 4 检查子进程是否已结束,设置并返回 returncode 属性。
 5 Popen.wait()
 6 等待子进程结束,设置并返回 returncode 属性。
 7 注意:如果子进程输出了大量数据到stdout或者stderr的管道,并达到了系统 pipe的缓存大小的话,子进程会等待父进程读取管道,而父进程此时正wait着的话,将会产生传说中的死锁,后果是非常严重滴。建议使用communicate()来 避免这种情况的发生。
 8 Popen.communicate(input=None)
 9 和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。可选的input如有 有的话,要为字符串类型。
10 此函数返回一个元组: (stdoutdata, stderrdata) 。
11 注意,要给子进程的stdin发送数据,则Popen的时候,stdin要为PIPE;同理,要可以收数据的话,stdout或者stderr也要为 PIPE。
12 注意:读到的数据会被缓存在内存里,所以数据量非常大的时候要小心了。
13 Popen.send_signal(signal)
14 给子进程发送signal信号量。
15 注意:windows下目前只支持发送SIGTERM,等效于下面的terminate()。
16 Popen.terminate()
17 停止子进程。Posix下是发送SIGTERM信号。windows下是调用TerminateProcess()这 个API。
18 Popen.kill()
19 杀死子进程。Posix下是发送SIGKILL信号。windows下和terminate()无异。
20 Popen.stdin
21 如果stdin参数是PIPE,此属性就是一个文件对象,否则为None。
22 Popen.stdout
23 如果stdout参数是PIPE,此属性就是一个文件对象,否则为None。
24 Popen.stderr
25 如果stderr参数是PIPE,此属性就是一个文件对象,否则为None。
26 Popen.pid
27 子进程的进程号。注意,如果shell参数为True,这属性指的是子shell的进程号。
28 Popen.returncode
29 子程序的返回值,由poll()或者wait()设置,间接地也由communicate()设置。
30 如果为None,表示子进程还没终止。
31 如果为负数-N的话,表示子进程被N号信号终止。(仅限*nux)

 生成 管道(“|”)

我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):

1 >>> import subprocess
2 >>> child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
3 >>> child2 = subprocess.Popen(["wc","-l"], stdin=child1.stdout,stdout=subprocess.PIPE)
4 >>> out = child2.communicate()
5 >>> print(out) 
6 (b'5\n', None)
7 >>> print((out[0]).decode('utf-8'))
8 5

 

child1.stdout-->subprocess.PIPE

child2.stdin<--subprocess.PIPE        

child2.stdout-->subprocess.PIPE

相当于将child1.stdout-->child2.stdin->child2.stdout->subprocess.PIPE

subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。

要注意的是,communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成。

 

posted @ 2017-05-16 17:00  Nice_keep-going  阅读(157)  评论(0)    收藏  举报