杰瑞科技汇

Python如何执行Linux命令?

在 Python 中执行 Linux 命令有几种常见的方法,每种方法都有其适用场景,下面我将详细介绍这些方法,并提供代码示例和最佳实践建议。

Python如何执行Linux命令?-图1
(图片来源网络,侵删)

方法概览

方法 函数 返回值 适用场景
os.system() os.system(command) 退出状态码 (整数) 简单执行命令,不关心命令的输出内容
os.popen() os.popen(command) 文件对象,可读取输出 需要获取命令的输出内容,但语法较旧
subprocess 模块 subprocess.run(), subprocess.Popen() CompletedProcess 对象或 Popen 对象 现代、灵活、安全,是官方推荐的方法
shutil 模块 shutil.which(), shutil.copy() 路径字符串等 用于执行特定的、与文件系统相关的命令

os.system() - 最简单直接

这是最简单、最古老的方法,它会在一个子shell中执行命令,并返回命令的退出状态码(0 表示成功,非 0 表示失败)。

特点:

  • 优点: 语法非常简单,适合快速执行不关心输出的命令。
  • 缺点: 无法直接获取命令的标准输出或标准错误内容,输出会直接打印到你的终端,不安全,容易受到 shell 注入攻击(如果命令参数来自用户输入)。

示例代码:

import os
# 执行一个简单的命令,不关心输出
# 返回值是命令执行后的退出码
return_code = os.system("ls -l")
print(f"命令执行退出码: {return_code}")
# 执行一个会出错的命令
return_code = os.system("ls /non_existent_directory")
print(f"错误命令的退出码: {return_code}")

os.popen() - 获取输出的旧方法

os.popen() 会打开一个管道,允许你像读取文件一样读取命令的输出。

Python如何执行Linux命令?-图2
(图片来源网络,侵删)

特点:

  • 优点: 可以方便地获取命令的标准输出。
  • 缺点: 语法较旧,功能有限,无法同时获取标准输出和标准错误,同样存在 shell 注入的安全风险。

示例代码:

import os
# 打开一个管道执行命令
# 'r' 表示以读模式打开
pipe = os.popen("ls -l")
# 读取命令的输出
output = pipe.read()
# 关闭管道
pipe.close()
print("命令输出:")
print(output)

subprocess 模块 - 现代推荐方法

subprocess 模块是 Python 中执行外部命令的首选和最强大的工具,它旨在替代旧的 os.systemos.popen 函数,提供了更强大、更灵活、更安全的接口。

subprocess.run() - 推荐的通用函数

这是 Python 3.5+ 引入的函数,是大多数情况下的最佳选择。

Python如何执行Linux命令?-图3
(图片来源网络,侵删)

关键参数:

  • args: 命令,可以是字符串或列表。强烈推荐使用列表形式,可以避免 shell 注入。
  • capture_output=True: 捕获标准输出和标准错误。
  • text=True: 将输出解码为文本(默认为字节)。
  • check=True: 如果命令返回非零退出码(即失败),则抛出 CalledProcessError 异常。
  • shell=False: 强烈建议保持为 False,除非你确实需要 shell 的特性(如管道、通配符)。

示例代码:

基本用法 - 获取输出

import subprocess
# 推荐使用列表形式传递命令,更安全
command = ["ls", "-l"]
# 执行命令并捕获输出
try:
    # capture_output=True 会捕获 stdout 和 stderr
    # text=True 会将输出解码为字符串
    result = subprocess.run(command, capture_output=True, text=True, check=True)
    # 访问标准输出
    print("标准输出:")
    print(result.stdout)
    # 访问标准错误
    # print("标准错误:")
    # print(result.stderr)
    # 访问返回码
    print(f"退出码: {result.returncode}")
except subprocess.CalledProcessError as e:
    # 当 check=True 且命令失败时,会抛出此异常
    print(f"命令执行失败,退出码: {e.returncode}")
    print(f"标准错误: {e.stderr}")

不关心输出,只关心是否成功

import subprocess
command = ["echo", "Hello from Python"]
# check=True 且命令失败,程序会在这里停止并报错
subprocess.run(command, check=True)
print("命令执行成功!")

subprocess.Popen() - 更底层的控制

当你需要更细粒度的控制时,比如与子进程进行实时交互(向一个程序发送输入),可以使用 Popen

示例代码:

import subprocess
# 启动一个进程,但不等待它完成
# Popen 返回一个 Popen 对象,你可以用它来管理进程
process = subprocess.Popen(["ping", "-c", "4", "google.com"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 等待进程结束,并获取输出
stdout, stderr = process.communicate()
print("进程已结束。")
print("标准输出:")
print(stdout)
print("标准错误:")
print(stderr)
print(f"退出码: {process.returncode}")

shutil 模块 - 特定命令的便捷方法

shutil 模块提供了一些高级的文件和目录操作函数,其中一些底层会调用系统命令。

示例代码:

import shutil
# 查找可执行文件的路径 (类似 which 命令)
python_path = shutil.which("python3")
print(f"python3 的路径是: {python_path}")
# 复制文件 (类似 cp 命令)
# shutil.copy("source.txt", "destination.txt")

安全警告:Shell 注入

当你使用 os.system 或将 shell=True 用于 subprocess 时,如果命令的任何部分来自用户输入,就可能存在Shell 注入的安全风险。

危险示例:

import subprocess
user_input = "malicious; rm -rf /" # 恶意输入
# 危险!这会执行 "ls -l malicious; rm -rf /"
# subprocess.run(f"ls -l {user_input}", shell=True) # 错误示范

安全做法:

始终将命令作为列表传递,并避免使用 shell=True

import subprocess
user_input = "malicious; rm -rf /"
# 安全!只会尝试查找一个名为 "malicious; rm -rf /" 的文件
# Shell 的特殊字符(如 ;, |, &)会被当作普通字符处理
subprocess.run(["ls", "-l", user_input]) # 正确示范

总结与最佳实践

场景 推荐方法 理由
快速执行一个命令,不关心输出 os.system() 代码最简单。
需要获取命令输出(旧代码) os.popen() 功能有限,不推荐用于新代码。
绝大多数情况 subprocess.run() 现代、灵活、安全,是官方推荐的首选。
需要与子进程实时交互 subprocess.Popen() 提供了对进程的底层控制能力。
查找可执行文件路径 shutil.which() 更 Pythonic,专为该任务设计。

核心建议:

  1. 优先使用 subprocess 模块。
  2. 始终将命令作为列表传递["ls", "-l"],而不是字符串 "ls -l",以防止 shell 注入。
  3. 除非必要,否则不要设置 shell=True
  4. 使用 subprocess.run(..., check=True) 可以优雅地处理命令执行失败的情况,避免手动检查返回码。
分享:
扫描分享到社交APP
上一篇
下一篇