Skip to main content
今日目标:Shell 虽然方便,但逻辑一复杂就成了”遗屎山”。今天学习 Python 的 subprocess 模块,学会如何在 Python 代码中优雅地执行系统命令,把 Shell 脚本重构为 Python 脚本。今天不只是写代码,而是要彻底理解 “为什么 subprocess 比 os.system 更安全”“如何正确捕获命令输出” 以及 “如何避免 Shell 注入攻击”

学习内容 (30 mins)

在开始写代码前,先搞懂这些核心概念,否则后面的代码你会看得云里雾里。
什么是 os.system?os.system() 是 Python 早期用于执行系统命令的方法,它会直接调用 Shell 执行命令。为什么不再推荐使用?
  • 无法获取输出
    • os.system() 只能返回状态码(0 表示成功,非 0 表示失败)
    • 无法获取命令的标准输出(stdout)和错误输出(stderr)
    • 输出直接打印到终端,无法在代码中处理
  • 安全隐患
    • 如果命令包含用户输入,容易被 Shell 注入攻击
    • 示例:os.system(f"rm {user_input}") 如果 user_input"file; rm -rf /" 就会执行危险操作
  • 跨平台问题
    • 不同操作系统的命令可能不同
    • Windows 和 Linux 的命令格式差异很大
替代方案:使用 subprocess 模块,更安全、更灵活。
什么是 subprocess?subprocess 是 Python 标准库中用于创建子进程、执行系统命令的模块,是执行外部命令的官方推荐方式。为什么 subprocess 更好?
  • 可以捕获输出:可以获取命令的标准输出和错误输出
  • 更安全:使用列表传参,避免 Shell 注入
  • 更灵活:可以控制输入、输出、错误处理
  • 跨平台:在不同操作系统上行为一致
核心方法
  • subprocess.run()(推荐):
    • 执行命令并等待完成
    • capture_output=True:捕获 stdout 和 stderr
    • text=True:自动将字节流解码为字符串
    • check=True:命令失败时自动抛出异常
    • timeout=秒数:设置超时时间
  • subprocess.Popen()(高级):
    • 用于需要实时处理输出的场景
    • 可以逐行读取输出,不等待命令完成
    • 适合长时间运行的命令
安全规范
  • 推荐:使用列表传参 subprocess.run(["ls", "-l"])
  • 禁止:使用字符串传参 subprocess.run("ls -l", shell=True)(除非必要)
  • 原因:列表形式避免了 Shell 解析,防止注入攻击

代码任务 (90 mins)

1

环境准备

确保虚拟环境已激活:
2

任务 A:基础命令执行

编写 11_run_cmd.py,演示 subprocess 的基本用法。
代码解释
  • 列表传参["ls", "-l"]"ls -l" 更安全
  • capture_output=True:捕获输出,可以在代码中处理
  • text=True:自动解码字节流为字符串
  • check=True:失败时自动抛出异常
  • timeout:防止命令无限执行
运行脚本
验证步骤
  1. 脚本能正常运行,无语法错误
  2. 观察命令输出是否正确
  3. 测试错误处理(如访问不存在的目录)
常见错误
  • FileNotFoundError: [Errno 2] No such file or directory - 命令不存在,检查命令路径
  • subprocess.TimeoutExpired - 命令执行超时,增加 timeout 或优化命令
  • subprocess.CalledProcessError - 命令执行失败,检查命令和参数
3

任务 B:磁盘监控脚本重构

将 Day 05 的 Shell 磁盘监控脚本重构为 Python 版本 11_disk_monitor.py
代码解释
  • 命令执行:使用 subprocess.run() 执行系统命令
  • 输出解析:使用字符串处理和正则表达式解析命令输出
  • 错误处理:捕获异常,优雅处理错误
  • 函数封装:将功能封装成函数,便于复用
运行脚本
验证步骤
  1. 脚本能正常运行,无语法错误
  2. 检查磁盘使用率输出是否正确
  3. 检查内存使用率输出是否正确
  4. 测试告警功能(如果使用率超过阈值)

拓展任务 (30 mins)

挑战 1:实时输出处理

任务:使用 subprocess.Popen 实现实时打印 ping 命令的输出。提示

挑战 2:命令管道

任务:在 Python 中实现 Shell 管道功能,如 ps aux | grep python提示:使用两个 Popen 对象,将第一个的输出连接到第二个的输入。

今日产出物

  • 11_run_cmd.py - subprocess 基础示例
  • 11_disk_monitor.py - 磁盘监控脚本(Python 版本)

参考代码

查看参考代码

在 GitHub 查看完整的示例代码

在线运行

使用在线编辑器测试代码

实际应用场景

subprocess 在运维中的应用

  • 系统监控:执行系统命令获取资源使用情况(CPU、内存、磁盘)
  • 服务管理:启动、停止、重启服务
  • 文件操作:批量处理文件、备份数据
  • 日志收集:执行命令收集日志信息
  • 自动化部署:执行部署脚本、运行测试

最佳实践

  • 使用列表传参:避免 Shell 注入攻击
  • 设置超时:防止命令无限执行
  • 捕获异常:优雅处理命令失败
  • 解析输出:使用正则表达式或字符串处理解析命令输出
  • 函数封装:将命令执行封装成函数,便于复用和测试
与 Day 12 的关联:今天学习的 subprocess,明天会学习如何用 Python 操作 MySQL 数据库,将系统监控和数据库存储结合起来。

上一天: 文件处理

Day 10 | 文件处理与正则实战

下一天: 数据库操作

Day 12 | Python 操作 MySQL