飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 认识Python

    • Python 历史与特点
    • Python 2 与 Python 3 的核心差异
    • 安装与运行 Python 2.7.18
    • 编码规范 PEP 8
  • 第2章 基础语法

    • 变量与对象
    • 数字类型
    • 字符串 str
    • Unicode 字符串
    • 运算符
    • 空值 None
  • 第3章 流程控制

    • if 条件语句
    • if-else 条件语句
    • if-elif-else 多分支
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 与 xrange
    • 循环控制:break、continue、pass
    • 循环 else 子句
  • 第4章 数据结构

    • 列表基础
    • 列表方法
    • 列表推导式
    • 元组
    • 字典基础
    • 字典方法
    • 字典循环技巧
    • 集合
    • 序列解包
    • 序列比较
  • 第5章 函数

    • 定义函数
    • 参数传递机制
    • 默认参数
    • 关键字参数
    • 可变参数
    • Lambda 表达式
    • 文档字符串
    • 函数对象
  • 第6章 模块与包

    • import 导入
    • 模块搜索路径
    • name 与主程序
    • 编译文件 .pyc 与 .pyo
    • 包结构
    • dir() 函数
  • 第7章 文件与IO

    • 打开与关闭文件
    • 文件读写方法
    • with 上下文管理器
    • 格式化输出:% 操作符
    • 格式化输出:str.format()
    • JSON 序列化
  • 第8章 面向对象

    • 类定义与实例化
    • init 构造方法
    • 类变量与实例变量
    • 方法调用与 self
    • 继承基础
    • 多重继承
    • 新式类与旧式类
    • 私有变量与名称改写
    • 属性装饰器 property
    • 类方法与静态方法
    • 魔术方法
    • 空类与数据记录
  • 第9章 异常处理

    • 异常类型
    • try-except
    • try-except-else-finally
    • 抛出异常 raise
    • 自定义异常
    • with 语句与上下文管理器
  • 第10章 迭代器与生成器

    • 迭代器协议
    • 生成器函数
    • 生成器表达式
    • itertools模块
  • 第11章 标准库精要

    • os模块
    • sys模块
    • datetime模块
    • re模块
    • json模块
    • collections模块
    • math与random模块
    • urllib2与网络请求
    • subprocess与命令执行
    • threading与并发
    • unittest与测试
    • 虚拟环境与包管理
  • 第12章 工程实践

    • 调试技巧
    • 性能分析
    • 文档与注释
    • 下一步学习

subprocess与命令执行

subprocess 模块是 Python 2.4+ 引入的,用于替代旧的 os.system、os.popen 等函数。它提供了更强大、更安全的进程创建和管理接口,支持输入输出重定向、管道、超时控制等功能。

基本用法

subprocess.call:执行命令,等待完成,返回退出码

import subprocess

result = subprocess.call(["ls", "-la"])
print result            # 0 表示成功

注意:参数用列表传递,避免 shell 注入。如果传入字符串,需要 shell=True:

# 安全方式(推荐)
subprocess.call(["echo", "hello"])

# 使用 shell(危险,避免用户输入)
subprocess.call("echo hello", shell=True)

subprocess.check_call:执行命令,失败时抛出 CalledProcessError

import subprocess

try:
    subprocess.check_call(["ls", "nonexistent"])
except subprocess.CalledProcessError as e:
    print "Command failed with code:", e.returncode

subprocess.check_output:执行命令,返回输出字符串

import subprocess

output = subprocess.check_output(["echo", "hello"])
print output            # hello\n

# 捕获错误输出
try:
    output = subprocess.check_output(
        ["ls", "nonexistent"],
        stderr=subprocess.STDOUT
    )
except subprocess.CalledProcessError as e:
    print "Error output:", e.output

Popen 类

Popen 提供更灵活的控制:

import subprocess

# 启动进程
process = subprocess.Popen(
    ["python", "-c", "print 'hello'"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

# 等待完成并获取输出
stdout, stderr = process.communicate()
print stdout            # hello\n
print process.returncode    # 0

实时读取输出:

import subprocess

process = subprocess.Popen(
    ["ping", "-c", "4", "example.com"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

# 逐行读取
for line in iter(process.stdout.readline, ''):
    print "Output:", line.strip()

process.wait()

输入输出重定向

import subprocess

# 写入 stdin
process = subprocess.Popen(
    ["cat"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE
)
stdout, _ = process.communicate("hello world")
print stdout            # hello world

# 重定向到文件
with open("output.txt", "w") as f:
    subprocess.call(["echo", "hello"], stdout=f)

# 从文件读取输入
with open("input.txt", "r") as f:
    subprocess.call(["cat"], stdin=f)

管道

import subprocess

# ps aux | grep python
p1 = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "python"], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()       # 允许 p1 在 p2 退出时接收 SIGPIPE

output = p2.communicate()[0]
print output

超时控制

Python 2.7 的 subprocess 没有内置超时,需要手动实现:

import subprocess
import signal

class TimeoutError(Exception):
    pass

def run_with_timeout(cmd, timeout_sec):
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
    def timeout_handler(signum, frame):
        process.kill()
        raise TimeoutError("Command timed out")
    
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout_sec)
    
    try:
        stdout, stderr = process.communicate()
        signal.alarm(0)     # 取消定时器
        return stdout, stderr, process.returncode
    except TimeoutError:
        return "", "Timeout", -1

stdout, stderr, code = run_with_timeout(["sleep", "10"], 2)
print code              # -1(超时)

注意:Windows 不支持 signal.SIGALRM,需要其他方式实现超时。

实际应用

执行 Git 命令:

import subprocess

def git_status():
    try:
        output = subprocess.check_output(["git", "status", "--short"])
        return output.strip().split("\n")
    except subprocess.CalledProcessError:
        return []
    except OSError:
        return []       # git 未安装

print git_status()

批量处理文件:

import subprocess
import os

def convert_images(directory):
    for filename in os.listdir(directory):
        if filename.endswith(".png"):
            input_path = os.path.join(directory, filename)
            output_path = input_path.replace(".png", ".jpg")
            
            subprocess.call([
                "convert", input_path, output_path
            ])
            print "Converted:", filename

检查命令是否存在:

import subprocess

def command_exists(cmd):
    try:
        subprocess.call([cmd, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return True
    except OSError:
        return False

print command_exists("python")      # True
print command_exists("nonexistent") # False

安全注意事项

永远不要将用户输入直接拼接到 shell 命令中:

# 危险!命令注入
user_input = "; rm -rf /"
subprocess.call("echo " + user_input, shell=True)   # 执行 echo; rm -rf /

# 安全
subprocess.call(["echo", user_input])               # 正确转义

使用列表参数传递时,subprocess 会自动处理转义,避免注入攻击。

与旧 API 的对比

旧 APIsubprocess 替代
os.system(cmd)subprocess.call(cmd, shell=True)
os.popen(cmd)subprocess.Popen(cmd, stdout=PIPE)
commands.getoutput(cmd)subprocess.check_output(cmd, shell=True)

subprocess 统一了所有进程调用方式,是 Python 2.7 中执行外部命令的首选。

上一页
urllib2与网络请求
下一页
threading与并发