Python2可用的subprocess PIPE控制Timidity++在Python3失效原因
解决Python3中subprocess控制Timidity++ Ncurses UI无响应的问题
我之前也踩过类似的坑,Python2和Python3在subprocess管道处理上的差异确实容易让人头疼。你的代码在Python2能正常工作,但Python3里失效,主要有两个核心原因,对应两种可行的解决方案:
原因1:Python3的管道缓冲机制差异
Python3中,subprocess.PIPE默认是全缓冲状态——你通过stdin.write()发送的命令会先存放在缓冲区里,不会立刻传递给Timidity++进程。而Python2里的管道缓冲策略更宽松,可能在write操作后自动触发了缓冲区刷新,所以命令能被进程及时接收。
解决方案1:手动flush缓冲区
每次执行write后,调用flush()强制把缓冲区的数据发送出去。修改后的代码如下:
from subprocess import Popen, PIPE import time a = Popen(["timidity", "-in", "MIDI_sample.mid"], stdin=PIPE, stdout=PIPE) time.sleep(4) a.stdin.write(b"+") a.stdin.flush() # 强制推送缓冲区数据到子进程 time.sleep(4) a.stdin.write(b"s") a.stdin.flush() time.sleep(4) a.stdin.write(b"s") a.stdin.flush() time.sleep(4) a.stdin.write(b"q") a.stdin.flush()
原因2:Timidity++的Ncurses UI需要终端交互
Timidity通过-in启用的Ncurses界面本质是终端应用,它期望连接到**伪终端(pty)**而非匿名管道。Python2的环境可能巧合地让管道模拟了终端行为,但Python3里这种兼容逻辑消失了,导致Timidity无法识别管道传来的命令。
解决方案2:使用伪终端(pty)交互
这种方式更可靠,能完全模拟终端环境。你可以选择用第三方库pexpect(更易用),或者Python标准库的pty模块。
方法A:用pexpect库(推荐)
先安装pexpect:
pip install pexpect
然后修改代码:
import pexpect import time # 用spawn创建带伪终端的子进程 child = pexpect.spawn("timidity -in MIDI_sample.mid") time.sleep(4) child.send("+") # 直接发送字符串,无需手动转字节 time.sleep(4) child.send("s") time.sleep(4) child.send("s") time.sleep(4) child.send("q")
方法B:用标准库pty模块
无需额外安装依赖,代码如下:
import pty import os import time def control_timidity(): # 创建伪终端并fork子进程 pid, fd = pty.fork() if pid == 0: # 子进程执行Timidity++ os.execvp("timidity", ["timidity", "-in", "MIDI_sample.mid"]) else: # 父进程通过伪终端发送命令 time.sleep(4) os.write(fd, b"+") time.sleep(4) os.write(fd, b"s") time.sleep(4) os.write(fd, b"s") time.sleep(4) os.write(fd, b"q") # 等待子进程结束 os.waitpid(pid, 0) control_timidity()
建议先尝试第一种方案(手动flush),如果还是不行,再用伪终端的方法——后者能彻底解决终端类应用的交互兼容问题。
内容的提问来源于stack exchange,提问作者Pomin Wu
相关产品推荐
相关产品推荐

