Rust终端模拟器库winpty-rs的使用:实现Windows平台下高效pty终端交互与控制

Rust终端模拟器库winpty-rs的使用:实现Windows平台下高效pty终端交互与控制

概述

winpty-rs是一个Rust库,用于在Windows平台上创建和管理伪终端(PTY)。它提供了对WinPTY和ConPTY两种后端实现的抽象,让开发者能够轻松地在Rust程序中实现终端模拟功能。

安装

在Cargo.toml中添加依赖:

[dependencies]
winpty-rs = "0.4"

需要确保系统PATH中包含winpty的可再发行二进制文件。

使用示例

基础使用示例

use std::ffi::OsString;
use winptyrs::{PTY, PTYArgs, MouseMode, AgentConfig};

// 创建PTY参数
let pty_args = PTYArgs {
    cols: 80,       // 终端列数
    rows: 25,       // 终端行数
    mouse_mode: MouseMode::WINPTY_MOUSE_MODE_NONE,  // 鼠标模式
    timeout: 10000,  // 超时时间(毫秒)
    agent_config: AgentConfig::WINPTY_FLAG_COLOR_ESCAPES  // 代理配置
};

// 创建PTY实例
let mut pty = PTY::new(&pty_args).unwrap();

// 启动cmd.exe
let cmd = OsString::from("cmd.exe");
pty.spawn(cmd, None, None, None).unwrap();

指定后端创建PTY

use winptyrs::{PTYBackend, PTY};

// 明确指定使用ConPTY后端
let conpty = PTY::new_with_backend(&pty_args, PTYBackend::ConPTY).unwrap();

// 明确指定使用WinPTY后端
let winpty = PTY::new_with_backend(&pty_args, PTYBackend::WinPTY).unwrap();

与PTY交互

// 向PTY写入数据
let command = OsString::from("dir\r\n");
pty.write(command).unwrap();

// 从PTY读取数据
if let Some(output) = pty.read(1024, false) {
    println!("Received: {}", output);
}

// 调整PTY大小
pty.set_size(120, 40).unwrap();

完整示例代码

下面是一个完整的终端交互示例,展示了如何创建PTY、启动进程并与之交互:

use std::ffi::OsString;
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use winptyrs::{PTY, PTYArgs, MouseMode, AgentConfig};

fn main() -> io::Result<()> {
    // 配置PTY参数
    let pty_args = PTYArgs {
        cols: 120,
        rows: 30,
        mouse_mode: MouseMode::WINPTY_MOUSE_MODE_NONE,
        timeout: 15000,
        agent_config: AgentConfig::WINPTY_FLAG_COLOR_ESCAPES,
    };

    // 初始化PTY
    let mut pty = PTY::new(&pty_args).map_err(|e| {
        io::Error::new(io::ErrorKind::Other, format!("PTY初始化失败: {}", e))
    })?;

    // 启动PowerShell进程
    let shell = OsString::from("powershell.exe");
    pty.spawn(shell, None, None, None).map_err(|e| {
        io::Error::new(io::ErrorKind::Other, format!("进程启动失败: {}", e))
    })?;

    // 等待初始输出
    thread::sleep(Duration::from_millis(800));
    if let Some(output) = pty.read(2048, false) {
        print!("{}", output);
    }

    // 发送命令获取系统信息
    let command = OsString::from("Get-ComputerInfo | Select-Object OsName, OsVersion, CsNumberOfProcessors, CsTotalPhysicalMemory\r\n");
    pty.write(command).map_err(|e| {
        io::Error::new(io::ErrorKind::Other, format!("命令写入失败: {}", e))
    })?;

    // 读取命令输出
    thread::sleep(Duration::from_millis(1000));
    if let Some(output) = pty.read(4096, false) {
        println!("系统信息:\n{}", output);
    }

    // 退出shell
    pty.write(OsString::from("exit\r\n")).map_err(|e| {
        io::Error::new(io::ErrorKind::Other, format!("退出命令失败: {}", e))
    })?;

    // 检查进程是否已退出
    match pty.is_alive() {
        Ok(alive) => println!("进程状态: {}", if alive { "运行中" } else { "已退出" }),
        Err(e) => eprintln!("获取进程状态失败: {}", e),
    }

    Ok(())
}

功能说明

  1. 多后端支持:自动选择最佳后端或手动指定WinPTY/ConPTY
  2. 终端控制:支持调整终端大小、鼠标模式等配置
  3. 异步通信:非阻塞读写操作
  4. 进程管理:可查询进程状态和退出码

注意事项

  1. 使用WinPTY后端时需要确保系统中有winpty.dll
  2. ConPTY需要Windows 10 1809或更高版本
  3. 读写操作需要注意缓冲区大小和适当延迟
  4. 错误处理应该考虑PTY特有的错误类型

1 回复

Rust终端模拟器库winpty-rs的使用:实现Windows平台下高效pty终端交互与控制

介绍

winpty-rs是Rust语言对winpty库的绑定,用于在Windows平台上实现伪终端(PTY)功能。它允许Rust程序创建和控制终端进程,实现类似Unix系统上的终端模拟功能。

主要特点:

  • 提供Windows平台上的伪终端支持
  • 支持进程创建和I/O交互
  • 可调整终端窗口大小
  • 支持ANSI转义序列处理

安装

在Cargo.toml中添加依赖:

[dependencies]
winpty = "0.2"

基本使用方法

创建终端进程

use winpty::{Config, Pty};

fn main() {
    // 创建配置
    let config = Config::default()
        .command("cmd.exe")  // 要运行的命令
        .dims(80, 25);       // 初始终端尺寸(列,行)
    
    // 创建PTY
    let mut pty = Pty::new(&config).expect("Failed to create PTY");
    
    // 获取进程ID
    println!("Spawned process with PID: {}", pty.process_id());
}

终端I/O交互

use std::io::{Read, Write};
use winpty::{Config, Pty};

fn main() {
    let config = Config::default().command("cmd.exe");
    let mut pty = Pty::new(&config).unwrap();
    
    // 获取输入输出管道
    let mut input = pty.input().unwrap();
    let mut output = pty.output().unwrap();
    
    // 写入命令
    input.write_all(b"echo Hello, winpty-rs!\r\n").unwrap();
    
    // 读取输出
    let mut buf = [0u8; 1024];
    let n = output.read(&mut buf).unwrap();
    println!("Output: {}", String::from_utf8_lossy(&buf[..n]));
}

调整终端大小

use winpty::{Config, Pty};

fn main() {
    let config = Config::default().command("cmd.exe");
    let mut pty = Pty::new(&config).unwrap();
    
    // 调整终端大小为100列×30行
    pty.set_size(100, 30).expect("Failed to resize terminal");
}

高级用法

处理ANSI转义序列

winpty-rs会自动处理ANSI转义序列,你可以直接使用颜色和其他终端特性:

use std::io::{Read, Write};
use winpty::{Config, Pty};

fn main() {
    let config = Config::default().command("cmd.exe");
    let mut pty = Pty::new(&config).unwrap();
    let mut input = pty.input().unwrap();
    let mut output = pty.output().unwrap();
    
    // 发送带颜色的命令
    input.write_all(b"color 0A && echo \x1b[31mRed Text\x1b[0m\r\n").unwrap();
    
    // 读取彩色输出
    let mut buf = [0u8; 1024];
    let n = output.read(&mut buf).unwrap();
    println!("Colored output: {}", String::from_utf8_lossy(&buf[..n]));
}

异步I/O处理

结合async/await实现异步终端交互:

use tokio::io::{AsyncReadExt, AsyncWriteExt};
use winpty::{Config, Pty};

#[tokio::main]
async fn main() {
    let config = Config::default().command("cmd.exe");
    let mut pty = Pty::new(&config).unwrap();
    
    let mut input = pty.input().unwrap();
    let mut output = pty.output().unwrap();
    
    // 转换为tokio的异步I/O
    let mut input = tokio::io::duplex(1024);
    let mut output = tokio::io::duplex(1024);
    
    // 异步写入
    input.write_all(b"dir\r\n").await.unwrap();
    
    // 异步读取
    let mut buf = vec![0u8; 1024];
    let n = output.read(&mut buf).await.unwrap();
    println!("Async output: {}", String::from_utf8_lossy(&buf[..n]));
}

完整示例Demo

下面是一个综合使用winpty-rs的完整示例,展示了终端创建、交互和异步处理:

use std::io::{Read, Write};
use std::thread;
use std::time::Duration;
use winpty::{Config, Pty};

fn main() {
    // 1. 创建PTY终端
    let config = Config::default()
        .command("cmd.exe")
        .dims(80, 25);
    
    let mut pty = Pty::new(&config).expect("创建PTY失败");
    
    println!("已创建终端进程,PID: {}", pty.process_id());
    
    // 2. 获取输入输出管道
    let mut input = pty.input().unwrap();
    let mut output = pty.output().unwrap();
    
    // 3. 发送命令并读取输出
    input.write_all(b"echo Hello from winpty-rs!\r\n").unwrap();
    
    let mut buf = [0u8; 1024];
    let n = output.read(&mut buf).unwrap();
    println!("终端输出:\n{}", String::from_utf8_lossy(&buf[..n]));
    
    // 4. 调整终端大小
    pty.set_size(100, 30).expect("调整终端大小失败");
    println!("已调整终端大小为100x30");
    
    // 5. 使用ANSI颜色
    input.write_all(b"color 0A && echo \x1b[31mThis is red text\x1b[0m\r\n").unwrap();
    
    let n = output.read(&mut buf).unwrap();
    println!("带颜色的输出:\n{}", String::from_utf8_lossy(&buf[..n]));
    
    // 6. 保持终端运行一段时间
    input.write_all(b"timeout /t 5\r\n").unwrap();
    thread::sleep(Duration::from_secs(7));
    
    println!("示例演示完成");
}

注意事项

  1. winpty-rs仅支持Windows平台
  2. 需要确保winpty.dll在可访问的路径中
  3. 处理长时间运行的进程时要注意资源释放
  4. 某些特殊字符可能需要额外转义处理

winpty-rs为Windows平台上的终端模拟提供了强大的支持,特别适合需要嵌入终端功能的Rust应用程序开发。

回到顶部