Python标准库-SubProcess

感谢Python标准库-SubProcess 提供的视频,本笔记仅仅用于自我记录,内容和视频一致

概述

subprocess 是Py调用外部工具的终极武器

在写 Python 代码时,你一定遇到过这些场景:

  • 想调用系统工具(如 gitffmpegping);

  • 想执行 Shell 命令批量处理文件;

  • 想运行其他程序或脚本,并获取它的输出;

  • 旧的 os.system()os.popen() 要么不安全,要么功能不够用。

  • 调用cmd

概述

其实你并不需要知道他有什么好处你只需要知道,他是一种调用外部工具的方法,以及怎么掌握调用外部工具就够了。

上面几种情况就需要 subprocess 标准库!它是 Python 3 推荐的执行外部命令 的库,功能强大、安全可控、跨平台兼容,能完全替代旧的 os.system()os.popen() 等方法!

本节课我们重点学习 subprocess 的核心功能:从基础的运行命令,到获取输出、处理错误、高级交互,最后还有一个综合实战案例!


代码演示

获取命令输入

1、核心概念和安全注意事项

  • 创建新的子进程,用来和新的子进程进行交互,获取子进程的输入、输出、错误和检查状态码。
  • 尽量不要使用shell=True这个特性
    2、基础操作
    如何运行外部命令 subprocess.run()实现
    subprocess.run(args,shell=True/False,check=False/True,capture_output=False/True,text=False/True)
py 复制代码
"""
subprocess 标准库学习
"""

import subprocess
# 1、核心概念和安全注意事项
# 创建新的子进程,用来和新的子进程进行交互,获取子进程的输入、输出、错误和检查状态码。
# 尽量不要使用shell=True这个特性
# 2、基础操作
# 如何运行外部命令 subprocess.run()实现
# subprocess.run(args,shell=True/False,check=False/True,capture_output=False/True,text=False/True)
# 查看当前目录下,有哪些文件和文件夹
result = subprocess.run(["dir"], shell=True)
print(f"result:>>>\n{result}")

小附录

平时写代码常用的参数有:

参数 作用
args 要执行的命令(列表或字符串),例如dirWindows 系统的命令 ,用来列出当前目录下的文件和文件夹 (相当于 Linux/Mac 下的 ls 命令)。
shell=True 是否通过系统shell执行(不推荐,有安全风险
check=True 如果命令执行失败(非0退出码),抛出异常
capture_output=True 捕获命令的输出和错误信息
text=True 输出以字符串形式返回(否则是字节)
input 给子进程提供输入数据
env 设置子进程的环境变量

核心概念和注意

1. subprocess 的作用

subprocess 用于在 Python 进程中创建新的子进程,执行外部命令或程序,并与它们交互(传递输入、获取输出 / 错误、检查返回码)。

2. 核心函数

  • subprocess.run()(推荐,Python 3.5+):最常用的函数,封装了大多数场景,简单易用。

  • subprocess.Popen(高级用法):底层类,更灵活,适合需要实时交互、长时间运行的命令。

3. ⚠️ 安全警告:避免命令注入

  • 尽量不要用 ****shell=True :除非必须用 Shell 特性(如管道、通配符),否则用命令列表 (如 ['ls', '-l']),避免命令注入攻击。

  • 命令注入示例(危险!):如果用户输入 "; rm -rf /",用 shell=True 会执行恶意命令!


运行外部命令(subprocess.run()

核心知识点

subprocess.run(args, ...) 是最基础的函数,参数:

  • args:命令(推荐用列表 ,如 ['ls', '-l'];也可以是字符串,但需配合 shell=True)。

  • shell:是否通过 Shell 执行(默认 False尽量保持 ****False)。

  • check:是否检查返回码(默认 False;设为 True 时,命令失败会抛出 CalledProcessError)。

  • capture_output:是否捕获标准输出 / 错误(默认 False)。

  • text(或 universal_newlines):是否用文本模式(默认 False,输出是 bytes;设为 True 输出是字符串)。

示例代码

python 复制代码
import subprocess
import sys

# ====================== 1. 运行简单命令(不捕获输出) ======================
print("===== 1. 运行 ls/dir 命令 =====")
# 适配 Windows 和 Linux/macOS
if sys.platform == "win32":
    # Windows 用 dir
    result = subprocess.run(["dir"], shell=True)  # Windows 下 dir 是 Shell 内置命令,需 shell=True
else:
    # Linux/macOS 用 ls
    result = subprocess.run(["ls", "-l"])

# 返回的是 CompletedProcess 对象
print("n命令参数:", result.args)
print("返回码(0=成功,非0=失败):", result.returncode)

# ====================== 2. 用 shell=True(仅当必要时!) ======================
print("n===== 2. 用 Shell 特性(如通配符) =====")
if sys.platform == "win32":
    # Windows 用 dir *.py
    result = subprocess.run("dir *.py", shell=True)
else:
    # Linux/macOS 用 ls *.py
    result = subprocess.run("ls *.py", shell=True)

内容讲解

  • 命令列表 vs 字符串

    • 列表(如 ['ls', '-l']):更安全,避免命令注入,推荐优先使用。

    • 字符串(如 'ls -l'):需配合 shell=True,仅当需要 Shell 特性(如通配符 *、管道 |、重定向 >)时使用。

  • Windows 注意事项dircopy 等是 Shell 内置命令,必须用 shell=True;而 pinggit 等是独立程序,不用 shell=True

  • 返回码returncode=0 表示命令成功,非 0 表示失败(不同命令的失败码含义不同)。


获取命令的输出(最常用场景!)

核心知识点

要获取命令的输出,用:

  • capture_output=True:同时捕获 stdout(标准输出)和 stderr(标准错误)。

  • 或者单独指定 stdout=subprocess.PIPEstderr=subprocess.PIPE

  • 必须加 ****text=True :否则输出是 bytes 类型(需要手动 decode())。

示例代码

python 复制代码
import subprocess
import sys

print("===== 获取命令输出 =====")
# 示例1:获取 ls -l / dir 的输出
if sys.platform == "win32":
    cmd = ["cmd", "/c", "dir"]  # Windows 下用 cmd /c 执行 dir,避免 shell=True
else:
    cmd = ["ls", "-l"]

# 捕获输出,文本模式
result = subprocess.run(
    cmd,
    capture_output=True,
    text=True,
    check=True  # 命令失败则抛出异常
)

print("标准输出:")
print(result.stdout)
print("标准错误:")
print(result.stderr)  # 成功时通常为空

# 示例2:获取 ping 的输出(注意平台参数差异)
print("n===== Ping 测试 =====")
if sys.platform == "win32":
    # Windows: ping -n 2 www.baidu.com
    ping_cmd = ["ping", "-n", "2", "www.baidu.com"]
else:
    # Linux/macOS: ping -c 2 www.baidu.com
    ping_cmd = ["ping", "-c", "2", "www.baidu.com"]

ping_result = subprocess.run(
    ping_cmd,
    capture_output=True,
    text=True
)
print("Ping 输出:")
print(ping_result.stdout)

内容讲解

  • stdout:命令的正常输出(如 ls 的文件列表)。

  • stderr:命令的错误信息(如文件不存在时的提示)。

  • text=True:Python 3.7+ 推荐用 text,旧版本用 universal_newlines(效果一样)。

  • Windows 下执行 dir 的技巧 :不用 shell=True,而是用 ["cmd", "/c", "dir"],更安全。


下面是原版

B 站 Python 授课:subprocess 标准库 ------Python 调用外部命令的 "终极武器"

课程导入

在写 Python 代码时,你一定遇到过这些场景:

  • 想调用系统工具(如 gitffmpegping);

  • 想执行 Shell 命令批量处理文件;

  • 想运行其他程序或脚本,并获取它的输出;

  • 旧的 os.system()os.popen() 要么不安全,要么功能不够用。

这时候就需要 subprocess 标准库!它是 Python 3 推荐的执行外部命令 的库,功能强大、安全可控、跨平台兼容,能完全替代旧的 os.system()os.popen() 等方法!

本节课我们重点学习 subprocess 的核心功能:从基础的运行命令,到获取输出、处理错误、高级交互,最后还有一个综合实战案例!


第一部分:核心概念与安全注意事项

1. subprocess 的作用

subprocess 用于在 Python 进程中创建新的子进程,执行外部命令或程序,并与它们交互(传递输入、获取输出 / 错误、检查返回码)。

2. 核心函数

  • subprocess.run()(推荐,Python 3.5+):最常用的函数,封装了大多数场景,简单易用。

  • subprocess.Popen(高级用法):底层类,更灵活,适合需要实时交互、长时间运行的命令。

3. ⚠️ 安全警告:避免命令注入

  • 尽量不要用 ****shell=True :除非必须用 Shell 特性(如管道、通配符),否则用命令列表 (如 ['ls', '-l']),避免命令注入攻击。

  • 命令注入示例(危险!):如果用户输入 "; rm -rf /",用 shell=True 会执行恶意命令!


第二部分:基础操作:运行外部命令(subprocess.run()

核心知识点

subprocess.run(args, ...) 是最基础的函数,参数:

  • args:命令(推荐用列表 ,如 ['ls', '-l'];也可以是字符串,但需配合 shell=True)。

  • shell:是否通过 Shell 执行(默认 False尽量保持 ****False)。

  • check:是否检查返回码(默认 False;设为 True 时,命令失败会抛出 CalledProcessError)。

  • capture_output:是否捕获标准输出 / 错误(默认 False)。

  • text(或 universal_newlines):是否用文本模式(默认 False,输出是 bytes;设为 True 输出是字符串)。

示例代码

python 复制代码
import subprocess
import sys

# ====================== 1. 运行简单命令(不捕获输出) ======================
print("===== 1. 运行 ls/dir 命令 =====")
# 适配 Windows 和 Linux/macOS
if sys.platform == "win32":
    # Windows 用 dir
    result = subprocess.run(["dir"], shell=True)  # Windows 下 dir 是 Shell 内置命令,需 shell=True
else:
    # Linux/macOS 用 ls
    result = subprocess.run(["ls", "-l"])

# 返回的是 CompletedProcess 对象
print("n命令参数:", result.args)
print("返回码(0=成功,非0=失败):", result.returncode)

# ====================== 2. 用 shell=True(仅当必要时!) ======================
print("n===== 2. 用 Shell 特性(如通配符) =====")
if sys.platform == "win32":
    # Windows 用 dir *.py
    result = subprocess.run("dir *.py", shell=True)
else:
    # Linux/macOS 用 ls *.py
    result = subprocess.run("ls *.py", shell=True)

内容讲解

  • 命令列表 vs 字符串

    • 列表(如 ['ls', '-l']):更安全,避免命令注入,推荐优先使用。

    • 字符串(如 'ls -l'):需配合 shell=True,仅当需要 Shell 特性(如通配符 *、管道 |、重定向 >)时使用。

  • Windows 注意事项dircopy 等是 Shell 内置命令,必须用 shell=True;而 pinggit 等是独立程序,不用 shell=True

  • 返回码returncode=0 表示命令成功,非 0 表示失败(不同命令的失败码含义不同)。


第三部分:获取命令的输出(最常用场景!)

核心知识点

要获取命令的输出,用:

  • capture_output=True:同时捕获 stdout(标准输出)和 stderr(标准错误)。

  • 或者单独指定 stdout=subprocess.PIPEstderr=subprocess.PIPE

  • 必须加 ****text=True :否则输出是 bytes 类型(需要手动 decode())。

示例代码

python 复制代码
import subprocess
import sys

print("===== 获取命令输出 =====")
# 示例1:获取 ls -l / dir 的输出
if sys.platform == "win32":
    cmd = ["cmd", "/c", "dir"]  # Windows 下用 cmd /c 执行 dir,避免 shell=True
else:
    cmd = ["ls", "-l"]

# 捕获输出,文本模式
result = subprocess.run(
    cmd,
    capture_output=True,
    text=True,
    check=True  # 命令失败则抛出异常
)

print("标准输出:")
print(result.stdout)
print("标准错误:")
print(result.stderr)  # 成功时通常为空

# 示例2:获取 ping 的输出(注意平台参数差异)
print("n===== Ping 测试 =====")
if sys.platform == "win32":
    # Windows: ping -n 2 www.baidu.com
    ping_cmd = ["ping", "-n", "2", "www.baidu.com"]
else:
    # Linux/macOS: ping -c 2 www.baidu.com
    ping_cmd = ["ping", "-c", "2", "www.baidu.com"]

ping_result = subprocess.run(
    ping_cmd,
    capture_output=True,
    text=True
)
print("Ping 输出:")
print(ping_result.stdout)

内容讲解

  • stdout:命令的正常输出(如 ls 的文件列表)。

  • stderr:命令的错误信息(如文件不存在时的提示)。

  • text=True:Python 3.7+ 推荐用 text,旧版本用 universal_newlines(效果一样)。

  • Windows 下执行 dir 的技巧 :不用 shell=True,而是用 ["cmd", "/c", "dir"],更安全。


第四部分:错误处理与返回码

核心知识点

  • check=True:如果命令返回码非 0(失败),会抛出 subprocess.CalledProcessError 异常。

  • 捕获 CalledProcessError 可以获取失败时的 returncodestdoutstderr

  • 如果不用 check=True,需要手动检查 result.returncode

示例代码

python 复制代码
import subprocess

print("===== 错误处理 =====")
# 示例:运行一个不存在的命令
try:
    result = subprocess.run(
        ["不存在的命令"],
        capture_output=True,
        text=True,
        check=True  # 失败则抛出异常
    )
except FileNotFoundError:
    print("❌ 错误:命令不存在!")
except subprocess.CalledProcessError as e:
    print(f"❌ 命令失败,返回码:{e.returncode}")
    print(f"错误输出:{e.stderr}")
else:
    print("✅ 命令成功!")
    print("输出:", result.stdout)

# 示例:手动检查返回码(不用 check=True)
print("n===== 手动检查返回码 =====")
result = subprocess.run(["ls", "不存在的文件"], capture_output=True, text=True)
if result.returncode == 0:
    print("✅ 成功")
else:
    print(f"❌ 失败,返回码:{result.returncode}")
    print(f"错误:{result.stderr}")

内容讲解

  • 常见异常

    • FileNotFoundError:命令不存在(如拼写错误)。

    • CalledProcessError:命令存在但执行失败(如 check=True 时返回码非 0)。

  • 手动检查 vs ****check=True

    • check=True 适合 "命令必须成功,否则中断程序" 的场景。

    • 手动检查适合 "命令失败也没关系,需要根据返回码做不同处理" 的场景。


第五部分:给命令提供输入

核心知识点

input 参数给命令传递输入(文本模式下是字符串,二进制模式下是 bytes)。

  • 适合需要交互式输入的命令(如 python 解释器、ssh 等)。

示例代码

python 复制代码
import subprocess

print("===== 给命令提供输入 =====")
# 示例:给 Python 解释器输入代码
python_code = """
print("Hello from subprocess!")
print(1 + 2 + 3)
"""

result = subprocess.run(
    ["python"],
    input=python_code,
    capture_output=True,
    text=True
)

print("Python 命令的输出:")
print(result.stdout)

内容讲解

  • input 参数会替代标准输入(sys.stdin),命令会从这里读取输入,而不是等待键盘输入。

  • 必须配合 text=True(如果输入是字符串)或传递 bytes(如 input=b"hellon")。


第六部分:高级用法:Popen 类(实时交互 / 长时间运行)

核心知识点

subprocess.Popen 是底层类,比 run() 更灵活,适合:

  • 需要实时获取输出的场景(如长时间运行的命令,边运行边打印输出)。

  • 需要与命令持续交互的场景(如发送多次输入)。

常用方法 / 属性

方法 / 属性 说明
Popen.poll() 检查命令是否完成(返回 None 表示未完成,返回 returncode 表示已完成)
Popen.wait() 等待命令完成(会阻塞)
Popen.communicate(input=None) 发送输入,获取输出 / 错误(会阻塞,适合一次性交互)
Popen.stdout / Popen.stderr 标准输出 / 错误的文件对象(可实时读取)

示例代码

python 复制代码
import subprocess
import sys
import time

print("===== Popen 实时获取输出 =====")
# 示例:运行 ping 命令,实时打印输出
if sys.platform == "win32":
    ping_cmd = ["ping", "-n", "4", "www.baidu.com"]
else:
    ping_cmd = ["ping", "-c", "4", "www.baidu.com"]

# 创建 Popen 对象,stdout=subprocess.PIPE,text=True
with subprocess.Popen(
    ping_cmd,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True,
    bufsize=1,  # 行缓冲
    universal_newlines=True
) as proc:
    # 实时读取 stdout
    print("Ping 实时输出:")
    for line in proc.stdout:
        print(line, end="")  # end="" 避免重复换行
    
    # 等待命令完成,获取 stderr
    stderr = proc.communicate()[1]
    if stderr:
        print("n错误输出:", stderr)
    
    print(f"n返回码:{proc.returncode}")

内容讲解

  • with 语句:自动管理 Popen 对象,确保资源释放。

  • bufsize=1:行缓冲,让输出实时显示(否则可能会等命令完成才一次性显示)。

  • communicate():适合一次性发送输入并获取输出,会阻塞直到命令完成。


第七部分:常见应用场景

  1. 调用系统工具 :如 git statusffmpeg 转码、tar 压缩文件。

  2. 批量处理文件:结合 Shell 命令批量重命名、转换文件格式。

  3. 检查系统状态 :如 ping 测试网络、df 查看磁盘空间、ps 查看进程。

  4. 调用其他脚本 / 程序:运行其他 Python 脚本、可执行文件,并获取结果。

  5. 自动化任务 :结合 cron(Linux)或 任务计划程序(Windows),定时执行命令。


第八部分:实战案例 ------ 简易 "系统命令执行器"

功能需求

结合 subprocess.run()Popen、跨平台适配,实现一个工具:

  1. 支持执行常见命令:

    • list:列出当前目录文件(ls/dir);

    • ping <host>:Ping 测试;

    • git:执行 git status(如果有 git);

  2. 跨平台适配(Windows/Linux/macOS);

  3. 捕获输出和错误,友好显示;

  4. 安全限制(只允许执行指定命令,避免任意命令执行)。

完整实战代码

python 复制代码
import subprocess
import sys

class SimpleCommandRunner:
    def __init__(self):
        self.platform = sys.platform

    def run_list(self):
        """列出当前目录文件"""
        print("===== 列出当前目录 =====")
        if self.platform == "win32":
            cmd = ["cmd", "/c", "dir"]
        else:
            cmd = ["ls", "-l"]
        
        self._run_cmd(cmd)

    def run_ping(self, host):
        """Ping 测试"""
        print(f"===== Ping {host} =====")
        if self.platform == "win32":
            cmd = ["ping", "-n", "3", host]
        else:
            cmd = ["ping", "-c", "3", host]
        
        self._run_cmd(cmd)

    def run_git_status(self):
        """执行 git status"""
        print("===== Git Status =====")
        try:
            self._run_cmd(["git", "status"])
        except FileNotFoundError:
            print("❌ 错误:未找到 git 命令,请先安装 git")

    def _run_cmd(self, cmd):
        """内部方法:执行命令并显示结果"""
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=True
            )
            print("✅ 命令成功!")
            print("输出:")
            print(result.stdout)
            if result.stderr:
                print("警告:", result.stderr)
        except subprocess.CalledProcessError as e:
            print(f"❌ 命令失败,返回码:{e.returncode}")
            print("错误输出:", e.stderr)
        except Exception as e:
            print(f"❌ 发生错误:{str(e)}")

    def show_usage(self):
        """显示用法"""
        print("===== 简易系统命令执行器 =====")
        print("用法:python command_runner.py <命令> [参数]")
        print("支持的命令:")
        print("  list          列出当前目录文件")
        print("  ping <host>   Ping 测试(如 ping www.baidu.com)")
        print("  git           执行 git status")
        print("  help          显示此帮助")

    def run(self):
        """主逻辑"""
        if len(sys.argv) < 2:
            self.show_usage()
            return
        
        command = sys.argv[1]
        if command == "list":
            self.run_list()
        elif command == "ping":
            if len(sys.argv) < 3:
                print("❌ 错误:ping 命令需要主机参数(如 ping www.baidu.com)")
                return
            host = sys.argv[2]
            self.run_ping(host)
        elif command == "git":
            self.run_git_status()
        elif command == "help":
            self.show_usage()
        else:
            print(f"❌ 错误:未知命令 '{command}'")
            self.show_usage()

# ========== 运行工具 ==========
if __name__ == '__main__':
    runner = SimpleCommandRunner()
    runner.run()

运行方式(命令行)

bash 复制代码
# 列出目录
python command_runner.py list

# Ping 测试
python command_runner.py ping www.baidu.com

# Git status
python command_runner.py git

# 显示帮助
python command_runner.py help

代码讲解

  1. 面向对象封装 :把所有功能封装在 SimpleCommandRunner 类里,结构清晰。

  2. 跨平台适配 :根据 sys.platform 选择不同的命令(如 dir vs ls-n vs -c)。

  3. 安全限制 :只允许执行指定的命令(list/ping/git),避免用户执行任意命令。

  4. 友好的错误处理 :捕获 FileNotFoundErrorCalledProcessError 等异常,给用户清晰的提示。

  5. 内部方法 ****_run_cmd:封装通用的命令执行逻辑,避免代码重复。


课程总结与避坑指南

核心知识点回顾

功能 方法 / 类 说明
基础运行命令 subprocess.run() 推荐,Python 3.5+
获取输出 capture_output=True + text=True 输出是字符串
错误处理 check=True + 捕获 CalledProcessError 命令失败时抛出异常
提供输入 input 参数 替代标准输入
高级交互 subprocess.Popen 实时输出、长时间运行

避坑指南

  1. 安全第一

    • 尽量用命令列表 (如 [&#39;ls&#39;, &#39;-l&#39;]),避免 shell=True

    • 不要让用户输入任意命令,限制可执行的命令范围。

  2. 平台差异

    • Windows 下注意 dir 等内置命令需用 cmd /c

    • pingls 等命令的参数在不同平台可能不同。

  3. 文本模式

    • 记得加 text=True,否则输出是 bytes,需要手动 decode()
  4. 资源管理

    • with 语句管理 Popen 对象,确保资源释放。

    • 长时间运行的命令注意实时读取输出,避免缓冲区填满导致阻塞。