subprocess

发布时间 2023-11-06 14:56:39作者: 龙泉寺老方丈

1. 简介

subprocess 是Python标准库中的模块,用于在Python程序中启动新的外部进程并与它们进行交互。这个模块提供了多种方法来执行外部命令,捕获命令的输出,处理标准输入和输出,以及管理进程的各个方面。以下是subprocess模块的一些关键功能和用途:

  1. 执行外部命令subprocess模块允许您在Python程序中执行外部命令,就像在命令行中执行一样。您可以指定要执行的命令以及任何参数。
  2. 捕获输出:您可以捕获外部命令的标准输出和标准错误输出,以便在Python中进一步处理或分析结果。
  3. 处理标准输入/输出subprocess允许您将数据传递给外部进程的标准输入,也允许您从标准输出和标准错误输出中读取数据。这对于与外部进程进行交互非常有用。
  4. 等待命令完成:您可以选择等待外部命令完成或不等待,并在后台执行命令。
  5. 进程管理subprocess提供了方法来获取和管理子进程的各种信息,如进程ID、状态、返回码等。
  6. 跨平台subprocess模块在不同的操作系统上都能工作,因此它是跨平台的解决方案。
  7. 异常处理subprocess模块可以处理与命令执行相关的异常,包括命令未找到、命令执行失败等情况。

subprocess模块是在Python中执行外部命令和与外部进程进行交互的关键工具之一。无论是执行命令行工具、处理数据流,还是管理外部进程,subprocess都能帮助您轻松完成这些任务。

2. subprocess.run

subprocess.run 是一个高级接口,用于执行外部命令并等待其完成。相较于 subprocess.Popensubprocess.run 的控制能力更有限,通常适用于简单的命令执行和获取结果的场景

2.1 应用场景

subprocess.run()是Python 3.5及更高版本中subprocess模块引入的函数,用于执行外部命令。
它的应用场景非常广泛,特别适用于以下情况:

  1. 执行外部命令:您可以使用subprocess.run()来执行系统中的外部命令,如调用其他可执行程序、脚本或工具。这可以用于自动化任务、系统管理和批处理作业等。
  2. 获取命令的标准输出和错误输出subprocess.run()允许您捕获命令的标准输出和错误输出,以便进一步处理或记录执行结果。
  3. 等待命令完成返回结果subprocess.run()会等待命令执行完成,然后返回一个包含有关执行结果的CompletedProcess对象,其中包括命令的返回码和执行时间等信息。
  4. 控制执行环境:您可以设置执行命令的工作目录、环境变量和输入数据。这使您可以在特定环境中运行命令,以及向命令提供输入数据。
  5. 安全执行命令subprocess.run()提供了一种更安全的方式来执行外部命令,它不依赖于系统shell,并且能够避免一些潜在的安全风险,如命令注入攻击。
  6. 异常处理subprocess.run()会引发异常,以便您可以捕获和处理命令执行期间可能发生的错误,如命令未找到或执行失败等情况。

2.2 常用参数

subprocess.run()函数有许多参数,用于控制执行外部命令的各个方面。下面是对这些参数的简要介绍:

  • args(必需):要执行的命令及其参数,可以是一个字符串或一个包含命令和参数的列表、元组。
  • stdin:用于输入数据的文件对象。如果不指定,输入数据可以通过input参数传递。
  • input:要传递给命令的输入数据,通常是一个字符串。如果指定了stdin参数,则input会被忽略。
  • stdout:用于捕获命令的标准输出的文件对象。如果不指定,可以使用capture_output=True来捕获输出。
  • stderr:用于捕获命令的标准错误输出的文件对象。如果不指定,可以使用capture_output=True来捕获输出。
  • capture_output:一个布尔值,用于指示是否捕获命令的标准输出和标准错误输出。如果为Truestdoutstderr参数会自动设置为subprocess.PIPE,以便捕获输出。
  • shell:一个布尔值,用于指示是否通过系统shell来执行命令。默认为False,即不通过系统shell。
  • cwd:要执行命令的工作目录。如果不指定,命令将在当前工作目录中执行。
  • timeout:命令的最大运行时间(以秒为单位)。如果命令在超时之前未完成,则会引发TimeoutExpired异常。
  • check:一个布尔值,用于指示是否在命令返回非零退出代码时引发CalledProcessError异常。默认为False,即不引发异常。
    • 返回值returncode,正常check=True时 returncode=0 代表结果都输出正常
  • encoding:要用于解码命令的标准输出和标准错误输出的编码。
  • errors:用于解码命令的标准输出和标准错误输出的错误处理方法。
  • text:一个布尔值,用于指示是否以文本模式打开标准输入、标准输出和标准错误。如果为True,则stdinstdoutstderr会自动以文本模式打开。
  • env:要传递给命令的环境变量字典。
  • universal_newlines:在Python 3中已废弃。在Python 2中,它指示是否以通用换行符模式打开标准输入、标准输出和标准错误。

2.3 示例

  • 获取输出
import subprocess

# 静默执行命令,禁用命令的标准输出
# result = subprocess.run(["ls", "-l"], cwd='/tmp', stdout=subprocess.DEVNULL)

# 不捕获输出,直接显示在终端
# result = subprocess.run(["ls", "-l"])

# 执行命令并捕获标准输出
result = subprocess.run(["ls", "-l"], cwd='/tmp', stdout=subprocess.PIPE, text=True)
# 获取命令的标准输出,执行结果
print(result.stdout)
print(result.returncode)

# 执行命令并捕获标准输出,标准错误输出
result = subprocess.run(["ls", "-l"], cwd='/tmp', capture_output=True, text=True)

# 将标准输出和标准错误输出重定向到文件output.txt和error.txt
with open("output.txt", "w") as stdout_file, open("error.txt", "w") as stderr_file:
    result = subprocess.run(["ls", "/tmp"], stdout=stdout_file, stderr=stderr_file, text=True
  • 根据不同命令执行结果输出不同颜色的结果
import subprocess
from colorama import Fore, init

# 初始化Colorama
init(autoreset=True)


def run_command(command, cwd=None, encoding=None):
    try:
        # 执行命令,捕获标准输出和返回码
        result = subprocess.run(command, cwd=cwd, shell=False, encoding=encoding, capture_output=True)

        # 如果命令成功执行,返回码为0,输出绿色文本
        if result.returncode == 0:
            print(Fore.GREEN + "Command executed successfully.")
            print(result.stdout)
        else:
            # 如果命令执行失败,返回码非0,输出红色文本
            print(Fore.RED + f"Command failed with return code {result.returncode}")
            print(result.stderr)

    except Exception as e:
        # 发生异常时输出黄色文本,并打印异常信息
        print(Fore.YELLOW + f"An error occurred: {str(e)}")
        exit(123)


# 调用优化后的函数来执行命令并显示不同颜色的文本
cmd = 'systeminfo'
enc = 'gbk'
run_command(command=cmd, encoding=enc)

3. subprocess.Popen

大多数情况下,推荐使用run()方法调用子进程,执行操作系统命令。在更高级的使用场景,你还可以使用 Popen 接口。其实run()方法在底层调用的就是 Popen 接口。

3.1 应用场景

subprocess.Popen 在以下场景中非常有用:

  1. 实时数据获取:如果您需要实时获取正在执行的命令的输出,subprocess.Popen 允许您在命令执行时逐行或逐块获取标准输出和标准错误输出。
  2. 并行执行多个命令:使用 subprocess.Popen,您可以同时启动多个子进程来执行不同的命令,而不需要等待一个命令完成后再执行另一个。
  3. 实时监控和处理命令输出:适用于需要监控命令的输出并基于输出采取实时措施的应用,例如日志记录、数据流处理等。
  4. 与正在运行的进程进行交互:如果您需要与正在运行的外部进程进行交互,例如向其发送输入或从中读取输出,subprocess.Popen 提供了管道和标准输入输出的控制。
  5. 执行复杂的任务:对于需要更高级的控制和管理的任务,例如长时间运行的进程、多个子进程之间的通信等,subprocess.Popen 提供了更大的灵活性。
  6. 高级命令行选项:如果您需要使用高级的命令行选项,通过 subprocess.Popen 可以更灵活地构建和调整命令参数。

总之,subprocess.Popen 对于需要更高级、更精确控制的命令执行和进程管理场景非常有用。它允许您以多种方式与外部进程进行交互,并以更灵活的方式处理命令的输入和输出。

3.2 常用参数

subprocess.Popen 是一个用于创建子进程来执行外部命令的函数,它接受一系列参数,用于配置和控制子进程的行为。以下是 subprocess.Popen 的一些常见参数:

  1. args:一个字符串或字符串列表,包含要执行的命令及其参数。
  2. stdin:指定子进程的标准输入,可以是一个文件对象或者 subprocess.PIPE(表示从父进程中获取输入)。
  3. stdout:指定子进程的标准输出,可以是一个文件对象、subprocess.PIPE(表示捕获子进程的输出)、subprocess.DEVNULL(表示丢弃输出)、None(继承父进程的输出)、或一个文件描述符(整数)。
  4. stderr:指定子进程的标准错误输出,可以是一个文件对象、subprocess.PIPEsubprocess.DEVNULLNone,或一个文件描述符。
  5. shell:一个布尔值,指示是否通过 shell 来执行命令。如果设置为 True,可以使用字符串形式的命令,否则应该使用字符串列表的形式。
  6. cwd:指定子进程的工作目录,用于执行命令。
  7. env:一个字典,用于指定子进程的环境变量。如果未指定,将使用父进程的环境变量。
  8. universal_newlines:一个布尔值,用于指示是否在文本模式下打开标准输入、输出和错误输出。
  9. encoding:用于解码标准输出和标准错误输出的字符编码。
  10. errors:用于处理解码错误的方式,通常为 strictignorereplace
  11. close_fds:一个布尔值,指示是否在子进程启动时关闭所有文件描述符。
  12. 其他参数:还有其他参数,如 preexec_fnstartupinfocreationflags 等,用于进一步控制子进程的行为。

3.3 方法

subprocess.Popensubprocess 模块提供的用于创建子进程的函数,它返回一个表示新进程的对象,允许你与该进程进行交互、控制其行为和处理其输入输出。下面是一些常用的 subprocess.Popen 方法和属性:

  1. communicate(input=None):这个方法用于与子进程交互,它将输入数据传递给子进程的标准输入,然后等待子进程完成,并返回一个包含标准输出和标准错误输出的元组。你可以通过传递 input 参数来提供标准输入的数据。
  2. wait():wait 方法用于等待子进程完成,然后返回子进程的返回码(退出状态码)。
  3. poll():poll 方法检查子进程是否已经结束。如果子进程已经结束,它返回子进程的返回码;如果子进程仍在运行,它返回 None。
  4. terminate():terminate 方法用于发送 SIGTERM 信号给子进程,以请求其终止。这通常用于正常终止进程。
  5. kill():kill 方法用于发送 SIGKILL 信号给子进程,以强制其立即终止。这通常用于异常情况下的进程终止。
  6. send_signal(signal):send_signal 方法用于发送任意信号给子进程。你可以通过这个方法发送其他信号,而不仅仅是 SIGTERM 或 SIGKILL。
  7. stdoutstderr:这些属性分别用于获取子进程的标准输出和标准错误输出。你可以从中读取子进程的输出数据。
  8. stdin:这个属性用于获取子进程的标准输入。你可以将数据写入这个文件对象,以提供标准输入的数据。
  9. returncode:这个属性用于获取子进程的返回码,表示子进程的退出状态码。

这些方法和属性允许你与子进程进行交互、等待其完成以及获取其输出和状态信息。根据你的需求,你可以使用这些方法来管理子进程的行为。

3.4 示例

  • 实时数据处理和监控:当需要实时处理外部命令的输出或监控正在执行的进程时,subprocess.Popen非常有用。例如,监视服务器日志文件、实时数据处理、或实时获取命令行工具的输出。
import subprocess

# ping连通性后将结果输出
process = subprocess.Popen('ping www.baidu.com', shell=False, stdout=subprocess.PIPE, encoding='gbk')

# 从管道获取结果并输出
while True:
    res = process.stdout.readline()
    if not res:
        break
    print(res)

# 等待命令执行完毕并获取返回码
return_code = process.wait()
print("Command executed with return code:", return_code)

-----------------------------------------------------------------

import subprocess

# 启动并实时监控一个命令的输出
process = subprocess.Popen("tail -f /var/log/server.log", shell=True, stdout=subprocess.PIPE)
for line in process.stdout:
    print(line.decode("utf-8").strip())

  • 批处理任务:如果需要按顺序执行一系列命令,并根据前一个命令的输出来决定下一个命令的操作,subprocess.Popen可以帮助你实现这种批处理任务。
import subprocess

# 依次执行多个命令
commands = ["command1", "command2", "command3"]
for cmd in commands:
    subprocess.Popen(cmd, shell=True).wait()
  • 与外部工具的集成:当需要与外部工具或服务进行交互时,subprocess.Popen可以用来启动和控制这些工具,以便与它们进行通信。
import subprocess

# 启动一个Python脚本并与其交互
process = subprocess.Popen(["python", "my_script.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output, _ = process.communicate(input="some_input".encode("utf-8"))
print(output.decode("utf-8"))