Python使用subprocess.Popen与pty在dumb终端运行交互式Bash的问题修复
问题根因说明
- 重复回显:宿主终端默认开启了输入回显,你输入的内容会先被本地终端打印一次;同时子bash绑定的PTY默认也开启了回显,输入写入PTY后bash会把输入内容再次回写到PTY master端,再被打印到stdout,就出现了重复回显。
- 快捷键行为异常:宿主终端默认工作在规范模式下,会自动捕获
Ctrl+C、Ctrl+D这类特殊按键,直接触发本地信号或EOF逻辑,不会把按键序列传给子bash进程,导致bash无法处理这些快捷键。
修复方案
在进入IO循环前修改宿主终端的属性,关闭本地回显、关闭规范模式、关闭本地信号捕获,所有输入直接透传给子PTY,回显和信号逻辑完全交给子bash处理。同时添加终端属性恢复逻辑,避免程序退出后终端工作异常。
修复后完整代码:
import os import pty import select import subprocess import sys import termios master_fd, slave_fd = pty.openpty() p = subprocess.Popen(['bash'], stdin=slave_fd, stdout=slave_fd, stderr=slave_fd, preexec_fn=os.setsid, env=dict(os.environ, TERM='dumb')) # 保存宿主终端原始属性 old_stdin_attr = termios.tcgetattr(sys.stdin.fileno()) try: # 修改宿主终端属性:关闭回显、关闭规范模式、关闭本地信号处理,不进入raw模式 new_stdin_attr = old_stdin_attr.copy() new_stdin_attr[3] = new_stdin_attr[3] & ~(termios.ECHO | termios.ICANON | termios.ISIG) termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, new_stdin_attr) while p.poll() is None: r, w, e = select.select([sys.stdin, master_fd], [], []) if sys.stdin in r: user_input = os.read(sys.stdin.fileno(), 10240) os.write(master_fd, user_input) elif master_fd in r: output = os.read(master_fd, 10240) os.write(sys.stdout.fileno(), output) finally: # 恢复宿主终端原始属性 termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, old_stdin_attr)
内容的提问来源于stack exchange,提问作者Flux
相关产品推荐
相关产品推荐

