Python 进程调用(subprocess)的实现
作者:cliffordl
1. 概述
1.1. subprocess 简介
在 Python 编程中,subprocess 库是一个功能强大的工具,它允许我们创建新进程、连接到其输入/输出/错误管道,并获取其返回代码。
1.2. subprocess 库的功能
subprocess 库提供了多种功能,以满足不同的需求。主要功能包括:
- 运行外部命令: 可以执行系统命令或调用其他可执行文件。
 - 交互式进程通信: 启动外部进程,并通过管道与其进行交互,发送输入并获取输出。
 - 管道和重定向: 创建一个进程的输出作为另一个进程的输入,或重定向进程的输出到文件。
 - 进程管理: 监控进程状态,等待进程结束,或终止进程。
 
2. 应用场景及示例
2.1. 运行外部命令
subprocess.run() 是一个高级接口,用于执行外部命令并等待其完成。它返回一个 CompletedProcess 对象,包含执行结果的相关信息。
import subprocess
import sys
def run_command_safely(command):
    """安全运行命令,处理Windows和Unix的差异"""
    try:
        if sys.platform == 'win32':
            # Windows 需要特殊处理
            result = subprocess.run(command, shell=True, capture_output=True, text=True, encoding='gbk')
        else:
            # Unix/Linux/Mac
            result = subprocess.run(command, capture_output=True, text=True)
        return result
    except Exception as e:
        return subprocess.CompletedProcess(command, 1, '', str(e))
    
result = run_command_safely(['ls'])
# # 执行外部命令并捕获输出
print("Return Code:", result.returncode)
print("Standard Output:", result.stdout)
print("Standard Error:", result.stderr)
capture_output=True:捕获子进程的标准输出和标准错误。
text=True:以文本模式处理输入/输出。
2.2. 交互式进程通信
subprocess.Popen() 提供了更灵活的方式来管理子进程,可以通过管道发送输入数据给子进程,并获取其输出。
import subprocess
import sys
def basic_popen(command):
    """基本Popen使用"""
    try:
        if sys.platform == 'win32':
            # 执行dir命令
            result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='gbk')
        else:
            result = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
    except Exception as e:
        return subprocess.CompletedProcess(command, 1, '', str(e))
    # 等待进程完成并获取输出
    stdout, stderr = result.communicate()
    # return result
    
    print(f"返回码: {result.returncode}")
    print(f"输出:\n{stdout}")
    if stderr:
        print(f"错误:\n{stderr}")
basic_popen(['dir'])
[‘dir’]:要执行的命令和参数。
stdin=subprocess.PIPE:通过管道向子进程发送数据。
stdout=subprocess.PIPE:通过管道捕获子进程的输出。
process.communicate(input=‘some input data\n’):向子进程发送输入数据并获取输出。
2.3. 管道和重定向
subprocess 库可以轻松创建管道,将一个进程的输出重定向到另一个进程的输入。
import subprocess
# 第一个命令
p1 = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)
# 第二个命令,通过管道传输数据
p2 = subprocess.Popen(['grep', '.py'], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0]
print('Python文件列表:')
print(output.decode())
p1.stdout=subprocess.PIPE:将p1的输出作为管道。
p2.stdin=p1.stdout:将p1的输出作为p2的输入。
p2.communicate():获取p2的最终输出。
2.4. 进程管理
subprocess.Popen() 还提供了多种方法来管理进程,如poll()、terminate()、kill()等。
import subprocess
import sys
def run_command_safely(command):
    """安全运行命令,处理Windows和Unix的差异"""
    try:
        if sys.platform == 'win32':
            # Windows 需要特殊处理
            result = subprocess.Popen(command, shell=True, text=True, encoding='gbk')
        else:
            # Unix/Linux/Mac
            result = subprocess.Popen(command)
        
        # 检查进程是否仍在运行
        print("Process is running:", result.poll() is None)
        # 终止进程
        result.terminate()
        # 等待进程结束
        result.wait()
        print("Process has terminated")
    except Exception as e:
        print("Exception", str(e))
        return subprocess.CompletedProcess(command, 1, '', str(e))
    
# 启动一个子进程
run_command_safely(['sleep', '10'])
process.poll():检查进程是否仍在运行,返回None表示仍在运行,返回退出码表示已结束。
process.terminate():请求终止进程。
process.wait():等待进程结束。
3. 与 os.system 和 os.popen 对比
虽然 os.system 和 os.popen 也可以用于执行外部命令并与进程进行交互,但它们与 subprocess 相比存在一些限制和不足。
3.1. os.system
os.system 是 Python 中最简单的执行外部命令的方法,但它有几个显著的缺点:
无法捕获输出: os.system 只能执行命令,无法直接捕获其输出或错误。输出会直接打印到控制台。
返回码有限: 它返回一个8位的字节,表示命令的退出状态,这可能导致状态码被截断。
安全性问题: 使用字符串拼接来构建命令可能会导致安全风险,如命令注入。
import os
# 执行外部命令
exit_code = os.system('dir')
print("Exit Code:", exit_code)
3.2. os.popen
os.popen 允许我们打开一个与命令的管道,从中读取输出。与 os.system 相比,它可以捕获输出,但仍然有一些限制:
单向通信: 它只能用于从命令读取输出,不能发送输入。
资源管理: 需要手动关闭文件对象,否则可能导致资源泄漏。
安全性问题: 同样存在命令注入的风险。
import os
# 打开一个与命令的管道并读取输出
process = os.popen('dir')
output = process.read()
process.close()
print("Output:", output)
3.3. subprocess 的优势
与 os.system 和 os.popen 相比,subprocess 提供了以下优势:
更强大的功能: 支持双向通信、管道、重定向等高级功能。
更好的资源管理: 可以自动管理文件描述符和进程,减少资源泄漏的风险。
更高的安全性: 通过列表形式传递命令和参数,避免了命令注入的风险。
更丰富的返回信息: subprocess.run() 返回一个 CompletedProcess 对象,包含退出码、输出和错误等信息。
到此这篇关于Python 进程调用(subprocess)的实现的文章就介绍到这了,更多相关Python 进程调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
