Python lib 之 Subprocess

484 阅读4分钟

subprocess模块是Python的标准库之一,提供了在Python中启动子进程并与其进行交互的功能。

使用subprocess模块可以创建新的进程或者打开已存在的进程,并执行一个命令,同时可以获取到进程的输入输出和错误输出。

subprocess常用功能有 方法subprocess.run() 和 类subprocess.Popen()

subprocess.run()

Python官网介绍

image.png

其他参数暂时不做赘述,这里只划出两个参数做简单说明:

  • capture_output

    If capture_output is true, stdout and stderr will be captured. The default value is False.

    从字面意思可以看出,当此参数设置为True时,我们可以获取命令(args)的执行结果。

image.png

当capture_output值为False时,如上图所示,命令执行成功,但stdout是空。

image.png

当capture_output值为True时,如上图所示,命令执行成功,stdout可被捕获。

  • shell

image.png

也就是说,shell的值与args的格式有一定的绑定关系:

shell 为 True:args可以为a single string

shell 为False:args为 a sequence of program arguments

image.png

如上图所示,当shell为False, args为‘a single string’时,系统报错。

Workaround1: string.split()

image.png

通过split()方法,将string转换为list后,subprocess.run()执行成功。

Workaround2: shell=True

image.png

subprocess.Popen()

subprocess.Popen,可以用于运行一个子进程,它可以调用系统的外部程序并连接到它们的输入/输出/错误管道,同时还可以收集它们的返回值和运行状态等信息。

Python官网介绍之Popen Constructor

image.png

常用参数与subprocess.run() 基本相同,重点介绍下stdin, stdout and stderr

stdin*, stdout and stderr: 参数常用值为PIPE,DEVNULL,None,File descriptor.

image.png

  • 与进程的单向通信

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

    # 直接执行命令输出到屏幕
    >>> subprocess.Popen("ls -l",shell=True)
    <subprocess.Popen object at 0x7fbde7541981>
    >>> total 10
    -rw-rw-r-- 1 nc nc    8 May 30 10:38 test
    
    # 不输出到屏幕,输出到变量
    >>> proc = subprocess.Popen(['echo','"Stdout"'],stdout=subprocess.PIPE)
    
    # communicate返回标准输出或标准出错信息
    >>> stdout_value = proc.communicate()
    >>> stdout_value
    (b'"Stdout"\n', None)
    
    >>> proc = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE)
    >>> stdout_value = proc.communicate()
    >>> stdout_value
    (b'total 8\ndrwxrwxr-x 4 nc nc 4096 May 25 11:50 Monitor\ndrwxrwxr-x 2 nc nc 4096 Mar 19 11:09 tmp\n', None)
    >>>
    >>> print((stdout_value[0]).decode('utf-8'))
    total 8
    drwxrwxr-x 4 nc nc 4096 May 25 11:50 Monitor
    drwxrwxr-x 2 nc nc 4096 Mar 19 11:09 tmp
    
    #将结果输出到文件
    >>> file_des = open("/home/nc/test.log",'w+')
    >>> subprocess.Popen("ls -l",shell=True,stdout=file_des)
    test.log:
    drwxrwxr-x 4 nc nc 4096 May 25 11:50 Monitor
    drwxrwxr-x 2 nc nc 4096 Mar 19 11:09 tmp
    
  • 与进程的双向通信

    以下实例相当于打开python3终端后,执行print,输出hello world至管道PIPE。

    >>> proc=subprocess.Popen(['python3'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    >>> out_value, err_value = proc.communicate(b'print("hello world")')
    >>> out_value
    b'hello world\n'
    >>> out_value.decode('utf-8')
    'hello world\n'
    
  • 记录下Popen.communicate()的使用说明

    image.png

  • 其他知识点参考官网:subprocess — Subprocess management — Python 3.11.3 documentation

subprocess.run 与 subprocess.Popen有区别么?

问了下gpt有关两者的区别,整理如下:

  1. 使用简易性:

    • subprocess.run(): 是一个在 Python 3.5 版本中引入的较新的便捷函数,用于执行一个命令并等待命令执行完成。它是执行子进程的推荐方式,封装了很多细节,使得执行单个命令变得简单。
    • subprocess.Popen(): 提供了更多的灵活性和控制能力。它可以用于启动一个子进程,并且不必立即等待该进程完成。这使得你可以在子进程运行的同时执行其他的 Python 代码,或者进行进程间通信。
  2. 返回值:

    • subprocess.run() 返回一个 subprocess.CompletedProcess 实例,它包含了执行命令后的状态,如返回码、输出和错误输出。
    • subprocess.Popen() 返回一个 Popen 对象,可以用来监控和控制子进程(例如通过 poll() 或 wait() 方法等待子进程完成,或者通过 communicate() 方法与之交互)。
  3. 使用场景:

    • 当你只需要简单地执行一个命令,并获取其结果时,使用 subprocess.run() 更加适合。
    • 当你需要对子进程的创建和管理有更多控制,例如异步执行、进程间通信或者同时启动多个子进程时,使用 subprocess.Popen() 更加合适。

举个例子,如果你想要运行 ls -l 命令并获取其输出,你可以使用 subprocess.run():

import subprocess

result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, text=True)
print(result.stdout)

但是,如果你想要运行同样的命令,并在命令执行的同时执行其他任务,或者你需要和子进程进行复杂的交互,那么你可能需要使用 subprocess.Popen():

import subprocess

with subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE, text=True) as proc:
    # 在这里可以执行其他任务
    stdout, _ = proc.communicate()
print(stdout)

在选择使用哪个函数时,需要考虑你的具体需求。对于大多数简单的用例,subprocess.run() 应该就足够了。如果你需要更多的灵活性和控制,那么 subprocess.Popen() 是更好的选择。