Python控制cmd时subprocess.read()无响应及死锁问题求助
Python 控制 CMD 遇到的管道死锁问题及解决方案
问题代码
cmd_process = subprocess.Popen(["cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='cp866') cmd_process.stdin.write('E:') cmd_process.stdin.flush() cmd_process.stdin.write('dir\n') cmd_process.stdin.flush()
遇到的异常情况
- 执行
print(cmd_process.stdout.read())时无任何输出,CMD进程陷入停滞,无法继续操作; - 循环10次调用
stdout.readline()可正常执行,后续能打印after for loop;但循环次数增加到1000次时,CMD进程停滞,后续代码无法执行。
1. 为什么stdout.read()没有任何输出?
原因有两点:
stdout.read()的逻辑是读取子进程的全部输出,直到子进程关闭stdout管道。但你启动的是交互式CMD进程,执行完dir后并没有退出,而是处于等待新输入的状态,stdout管道始终未关闭,导致read()一直阻塞等待,自然不会返回任何输出。- 你发送的
E:命令没有加换行符,CMD需要换行来确认执行命令,所以实际盘符并未切换,后续dir还是在原盘符执行;加上管道阻塞的问题,最终read()完全拿不到有效输出。
2. 如何解决该死锁问题?
根据场景不同,有三种可靠的解决方案:
方案1:使用Popen.communicate()(官方推荐,非交互式场景)
communicate()会一次性发送所有输入,自动读取stdout和stderr的全部输出,同时处理管道缓冲区,从根源避免死锁。注意给每个命令加换行符,确保CMD执行:
import subprocess cmd_process = subprocess.Popen(["cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='cp866') # 将命令合并为带换行的字符串,确保CMD依次执行 output, error = cmd_process.communicate(input='E:\ndir\n') print(output) print(error)
该方法会等待子进程结束后返回输出,适合一次性执行命令的场景。
方案2:用线程单独处理输出(交互式场景)
如果需要保持CMD进程持续运行并交互,可以启动两个线程分别读取stdout和stderr,避免缓冲区填满导致阻塞:
import subprocess import threading def read_output(stream, callback): for line in iter(stream.readline, ''): callback(line) cmd_process = subprocess.Popen(["cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='cp866') # 启动后台线程读取输出 threading.Thread(target=read_output, args=(cmd_process.stdout, print), daemon=True).start() threading.Thread(target=read_output, args=(cmd_process.stderr, print), daemon=True).start() # 发送命令,务必加换行符 cmd_process.stdin.write('E:\n') cmd_process.stdin.flush() cmd_process.stdin.write('dir\n') cmd_process.stdin.flush() # 保持主线程运行,避免程序提前退出 input("按回车退出...") cmd_process.stdin.write('exit\n') cmd_process.stdin.flush()
线程会持续读取输出,不会让缓冲区阻塞,适合需要长期交互的场景。
方案3:使用高层API(一次性命令场景)
如果只是执行一组命令后退出,用subprocess.check_output()更简洁,它直接返回命令输出,自动处理管道细节:
import subprocess # 用cmd /c执行命令序列,多个命令用&分隔 output = subprocess.check_output(['cmd', '/c', 'E: & dir'], encoding='cp866') print(output)
cmd /c会在执行完所有命令后自动退出进程,避免进程挂起。
内容的提问来源于stack exchange,提问作者mischa-94
相关产品推荐
相关产品推荐

