Python subprocess的使用

前言

部门内部存在大量代码使用Python去调用Shell或者JS脚本,因此重度依赖subprocess(使用Google的subprocess32),在使用subprocess的时候存在一些疑问。包括为什么使用shell=TruePopen类如何使用等等。希望通过本篇文章,让自己掌握subprocess的使用。

使用

subprocess用于创建子进程去运行我们指定的可执行程序(文件、脚本)。核心是用Popen类。

常用的函数

(1)执行命令的函数

Python2

函数名 函数签名
check_call subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, **other_popen_kwargs)
check_output subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, universal_newlines=None, timeout=None, text=None, **other_popen_kwargs)

check_call用于调用命令,会抛CalledProcessError。check_output会返回stdout,如果要把stderr也重定向到stdout,可以指定参数stderr=subprocess.STDOUT。两个函数的签名和Popen的构造函数参数类似。

Python3

函数名 函数签名
run subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs)

(2)Popen

以上的函数调用都是同步的,我们只能等待命令执行结束才能执行其他任务。想要异步的去运行一些比较耗时的命令可以使用更加强大的Popen类。

构造函数

构造函数签名
class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None, text=None)

参数很多,常用的参数包括

args: 该参数可以是一个列表或者一个字符串。当传递一个列表时,列表的首元素应该是要执行的脚本或者可执行程序,其他元素是可执行程序的参数。

​ 如果传递的是字符串,则建议设置Shell=True。否则只能执行字符串为不带参数的可执行程序(Popen("pwd"))。

shell=True: 如果希望直接使用shell去执行命令,可以指定shell=True。指定shell=True等同于 Popen(['/bin/sh', '-c', args[0], args[1], ...])。

常用方法

函数名 函数签名
poll Popen.poll()
communicate Popen.communicate(input=None, timeout=None)
wait Popen.wait(timeout=None)

poll: 检查子进程是否结束,子进程结束则返回 returncode属性,否则返回None

wait: 等待子进程结束

communicate: 读取子进程的stdout和stderr,向stdin写入。函数返回一个元组(stdout_data, stderr_data)。

实例:

#!/usr/bin/env python
# coding=utf-8

from subprocess import Popen, TimeoutExpired, PIPE
from shlex import split

def count_lines(text):
    """
    count how much line in text
    """
    proc = Popen(split("wc -l"), stdout=PIPE, stdin=PIPE)
    print(bytes(text, encoding="utf-8"))
    #向子进程的标准输入写入数据,使用flush冲刷缓冲区
    proc.stdin.write(bytes(text, encoding="utf-8"))
    proc.stdin.flush()
    return proc

procs = []
for i in range(5):
    procs.append(count_lines("abcefcdnfsdf\n")) 

for proc in procs:
    #从子进程的标准输入读取
    stdout, stderr = proc.communicate()
    print(str(stdout), stderr)

执行结果:
Python subprocess的使用

上一篇:Linux shell编程(五): Linux文件权限管理


下一篇:shell基础之多文件编程