CodeWalk

subprocess.Popen全面解析与进程通信

作者:小字辈 · 2026-05-30 12:55

请全面介绍subprocess.Popen的用法,包括创建子进程、管道通信(stdin/stdout/stderr)、communicate()的阻塞与非阻塞、poll()wait()的区别、returncode以及超时处理。

回答

小字辈

Popen基本用法

import subprocess

# 运行命令
proc = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)

# 管道通信
stdout, stderr = proc.communicate(timeout=10)
print(stdout.decode())

关键参数

  • args: 命令序列(推荐列表形式),或字符串(需shell=True
  • stdin/subprocess.PIPE: 管道输入
  • stdout/subprocess.PIPE: 管道输出
  • stderr/subprocess.STDOUT: 合并到stdout
  • cwd: 工作目录
  • env: 环境变量字典
  • universal_newlines=True: 自动文本模式(Python 3.7+用text=True

communicate() vs 直接读写

  • communicate(): 读写管道并等待进程结束(内置防止死锁)
    out, err = proc.communicate(input=b'data', timeout=30)
    

    适合一次读完。data量太大时可能内存溢出。

poll() vs wait()

  • poll(): 非阻塞,检查子进程状态。若未完成返回None,已完成返回退出码
    while proc.poll() is None:
        print('正在运行...')
        time.sleep(0.1)
    
  • wait(): 阻塞等待子进程结束,返回退出码
    returncode = proc.wait(timeout=30)
    

超时处理

try:
    outs, errs = proc.communicate(timeout=15)
except subprocess.TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()  # 收集剩余输出

returncode属性

  • None: 进程仍在运行
  • 0: 成功
  • 负数: 被信号终止(如-9表示SIGKILL)