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

如何在Rust中读取ANSI DSR查询响应(纯标准库无libc依赖方案)

解决方案

核心问题

当前实现无法读取响应的根因如下:

  • print! 宏默认使用行缓冲,未手动刷新的情况下DSR请求序列不会真正发送到终端
  • 终端默认处于规范模式,会缓存输入直到遇到换行符才返回给程序,且默认开启输入回显,DSR响应属于无换行的控制序列,自然无法被读取
  • 读线程中冗余的if buf != [0u8]判断可能拦截合法响应字节

修复实现

以下为符合要求的纯Rust实现,仅依赖标准库,可选择使用纯Rust系统调用库rustix替代libc实现完全无C依赖:

use std::sync::mpsc::{channel, Receiver, TryRecvError};
use std::{
    io::{self, Read, Write},
    panic, thread, time,
};
use libc::{tcgetattr, tcsetattr, termios, ECHO, ICANON, TCSANOW, STDIN_FILENO};

fn main() {
    // 保存原有终端属性
    let mut original_termios = std::mem::MaybeUninit::<termios>::uninit();
    unsafe { tcgetattr(STDIN_FILENO, original_termios.as_mut_ptr()); }
    let original_termios = unsafe { original_termios.assume_init() };

    // 切换终端为原始模式:关闭规范输入、关闭回显,让终端直接返回响应
    let mut raw_termios = original_termios;
    unsafe {
        raw_termios.c_lflag &= !(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios);
    }

    // 捕获panic确保终端属性被恢复
    let run_result = panic::catch_unwind(|| {
        let timeout = time::Duration::from_millis(1);
        let rx = spawn_read();
        
        // 发送请求并立刻刷新缓冲,确保请求到达终端
        print!("\x1b[19t");
        io::stdout().flush().unwrap();

        let mut data = Vec::new();
        // 最多等待100ms,足够接收终端响应
        for _ in 0..100 {
            thread::sleep(timeout);
            match rx.try_recv() {
                Ok(value) => {
                    data.push(value);
                    // 收到响应终止符t直接提前退出
                    if value == b't' {
                        break;
                    }
                },
                Err(TryRecvError::Empty) => {},
                Err(TryRecvError::Disconnected) => break,
            }
        }
        data
    });

    // 强制恢复原有终端属性,避免终端行为异常
    unsafe { tcsetattr(STDIN_FILENO, TCSANOW, &original_termios); }

    let data = run_result.unwrap();
    let string = match std::str::from_utf8(&data) {
        Ok(value) => value,
        Err(_) => "",
    };
    println!("Captured: {:?}", string);
}

fn spawn_read() -> Receiver<u8> {
    let (tx, rx) = channel::<u8>();
    thread::spawn(move || loop {
        let mut buf = [0u8];
        if io::stdin().read_exact(&mut buf).is_ok() {
            tx.send(buf[0]).ok();
        } else {
            break;
        }
    });
    rx
}

注意事项

  • 该方案仅在真实tty终端环境下生效,管道、重定向、部分IDE内置终端、不支持DSR的远程终端场景下无法拿到响应
  • Windows平台可通过调用Win32 API的GetConsoleMode和SetConsoleMode关闭行输入、回显开关,逻辑和Unix平台完全一致
  • 若需要完全无libc依赖,可使用纯Rust实现的系统调用库rustix替换上述代码中的libc调用,无需修改核心逻辑

内容的提问来源于stack exchange,提问作者Brian Bruggeman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:45:05