subprocess模块允许你启动一个新的进程,连接输入/输出/错误的管道,
获得子进程的返回码。这个模块目标是代替一些老的模块,比如os.system和os.spawn.
0x01 常见subprocess方法
subprocess模块中的常用函数
函数描述
subprocess.run()
Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
subprocess.call()
执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。
subprocess.check_call()
Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(..., check=True)。
subprocess.check_output()
Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
subprocess.getoutput(cmd)
接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
subprocess.getstatusoutput(cmd)
执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False)
args该参数用于启动进程。这可能是一个列表或一个字符串。
returncode 该参数表示子进程的退出状态。
通常情况下,0作为退出状态表明它成功运行。
负值-N表明子进程被信号N终止(仅POSIX)。
stdout 该参数表示标准输出
此调用方式返回CompletedProcess实例,和直接Popen 差不多,实现是一样的,实际也是调用Popen,与Popen构造函数大致相同,例如:
>>> subprocess.run(["ls", "-l"]) # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
subprocess.call()
执行由参数提供的命令.
我们可以用数组作为参数运行命令,也可以用字符串作为参数运行命令(通过设置参数shell=True)
注意,参数shell默认为False
subprocess.call(['ipconfig','/all'])
subprocess.call('ipconfig /all',shell=True)
subprocess.getoutput(cmd)
cmd可以直接执行shell命令,而不需要cmd命令以列表输入---subprocess.getoutput("cat /proc/meminfo")
返回值包含cmd的执行结果,可以直接赋值给某个变量
功能和getstatusoutput类似
a=subprocess.getoutput('ipconfig /all')print(a)
subprocess.getstatusoutput(cmd)
cmd可以直接执行shell命令,而不需要cmd命令以列表输入----subprocess.getstatusoutput("cat /proc/meminfo")
返回值包含cmd的执行状态和执行结果,可以直接赋值给某个变量,返回的是一个元组
a=subprocess.getstatusoutput('ipconfig /all')print(a)
subprocess.check_call()
check_call基本和call功能一样,只是增加了返回状态码校验,如果执行状态码是0,则返回0,否则抛出异常
subprocess.check_call(['ipconfig', '/all'])
subprocess.check_output()
执行命令,如果状态码是0,则返回执行结果,否则抛出异常--
a=subprocess.check_output(['ipconfig', '/all'])print(a)
0x02、subprocess.Popen()类
该类用于在一个新的进程中执行一个子程序。前面我们提到过,上面介绍的这些函数都是基于subprocess.Popen类实现的,通过使用这些被封装后的高级函数可以很方面的完成一些常见的需求。由于subprocess模块底层的进程创建和管理是由Popen类来处理的,因此,当我们无法通过上面哪些高级函数来实现一些不太常见的功能时就可以通过subprocess.Popen类提供的灵活的api来完成。
1.subprocess.Popen的构造函数
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, encoding=None, errors=None)
参数说明:
args: 要执行的shell命令,可以是字符串,也可以是命令各个参数组成的序列。当该参数的值是一个字符串时,该命令的解释过程是与平台相关的,因此通常建议将args参数作为一个序列传递。
bufsize: 指定缓存策略,0表示不缓冲,1表示行缓冲,其他大于1的数字表示缓冲区大小,负数 表示使用系统默认缓冲策略。
stdin, stdout, stderr: 分别表示程序标准输入、输出、错误句柄。
preexec_fn: 用于指定一个将在子进程运行之前被调用的可执行对象,只在Unix平台下有效。
close_fds: 如果该参数的值为True,则除了0,1和2之外的所有文件描述符都将会在子进程执行之前被关闭。
shell: 该参数用于标识是否使用shell作为要执行的程序,如果shell值为True,则建议将args参数作为一个字符串传递而不要作为一个序列传递。
cwd: 如果该参数值不是None,则该函数将会在执行这个子进程之前改变当前工作目录。
env: 用于指定子进程的环境变量,如果env=None,那么子进程的环境变量将从父进程中继承。如果env!=None,它的值必须是一个映射对象。
universal_newlines: 如果该参数值为True,则该文件对象的stdin,stdout和stderr将会作为文本流被打开,否则他们将会被作为二进制流被打开。
startupinfo和creationflags: 这两个参数只在Windows下有效,它们将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如主窗口的外观,进程优先级等。
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:
>>> importsubprocess>>>
>>> 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 devtmpfs3.9G 0 3.9G 0% /dev
tmpfs tmpfs3.9G 0 3.9G 0% /dev/shm
tmpfs tmpfs3.9G 386M 3.5G 10% /run
tmpfs tmpfs3.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) \n')>>> obj.stdin.write('print(2) \n')>>> obj.stdin.write('print(3) \n')>>> 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) \n')>>> 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