为什么自定义SSH封装脚本中运行tmux、vim会输出乱码?
问题根因
你看到的乱码是终端控制应答序列:tmux、vim这类全屏终端程序启动后会主动向终端发送特性查询、光标位置查询等CSI控制指令,原生终端收到这类指令后会悄悄返回应答给程序,不会将应答内容打印到屏幕上。
你的脚本没有修改本地终端的运行模式,本地终端默认处于「行缓冲+输入回显」的标准模式,所以会把本该传给远程程序的应答序列直接输出到屏幕,就出现了你看到的乱码。
修复方案
使用Python标准库的termios、tty模块修改本地终端模式,程序退出前恢复原始终端属性即可,同时优化二进制读写避免编码错误,修改后的完整代码如下:
import os import pty import select import signal import subprocess import sys import termios import tty master_fd, slave_fd = pty.openpty() process = subprocess.Popen(['ssh'] + sys.argv[1:], stdin=slave_fd, stdout=slave_fd, stderr=subprocess.STDOUT, preexec_fn=os.setsid) # 保存本地原始终端属性,退出时恢复 orig_tty_attr = termios.tcgetattr(sys.stdin.fileno()) def restore_tty(): termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, orig_tty_attr) def sigint_handler(_signum, _frame): os.write(master_fd, b'\03') # Send Ctrl-c. signal.signal(signal.SIGINT, sigint_handler) def sigtstp_handler(_signum, _frame): os.write(master_fd, b'\x1A') # Send Ctrl-z. signal.signal(signal.SIGTSTP, sigtstp_handler) def sigchld_handler(_signum, _frame): restore_tty() process.wait() sys.exit(process.returncode) signal.signal(signal.SIGCHLD, sigchld_handler) # 处理窗口大小变化,同步给远程终端 def sigwinch_handler(_signum, _frame): winsize = termios.tcgetwinsize(sys.stdin.fileno()) termios.tcsetwinsize(master_fd, winsize) signal.signal(signal.SIGWINCH, sigwinch_handler) try: # 将本地终端设置为raw模式,关闭输入回显、行缓冲 tty.setraw(sys.stdin.fileno()) # 初始化同步一次窗口大小 termios.tcsetwinsize(master_fd, termios.tcgetwinsize(sys.stdin.fileno())) while process.poll() is None: r, w, e = select.select([sys.stdin, master_fd], [], []) if sys.stdin in r: user_input = os.read(sys.stdin.fileno(), 4096) if not user_input: os.write(master_fd, b'\04') # Send Ctrl-d. else: os.write(master_fd, user_input) if master_fd in r: data = os.read(master_fd, 4096) # 直接写二进制到标准输出,避免解码出错 sys.stdout.buffer.write(data) sys.stdout.flush() finally: # 任何退出场景都恢复原始终端属性 restore_tty()
修改点说明
- 新增终端属性保存/恢复逻辑,避免脚本退出后本地终端无法正常使用
- 启动时将本地终端设置为raw模式,终端会自动处理控制序列的应答传递,不再打印乱码
- 直接操作标准输入输出的二进制buffer,避免特殊控制字符解码出错
- 新增窗口大小变化信号处理,远程tmux、vim会自动适配窗口大小
内容的提问来源于stack exchange,提问作者Flux
相关产品推荐
相关产品推荐

