You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 02:06:04