#自动化 #测试 #pty #expect #子进程 #pexpect

开发 rexpect

以与 pexpect 或 Don libes expect 相同的方式与 Unix 进程/bash 进行交互

6 个版本 (重大更新)

0.5.0 2022 年 10 月 20 日
0.4.0 2020 年 5 月 25 日
0.3.0 2018 年 2 月 25 日
0.2.0 2017 年 9 月 27 日
0.1.0 2017 年 7 月 27 日

#67 in Unix API

Download history 2763/week @ 2024-04-20 2253/week @ 2024-04-27 3454/week @ 2024-05-04 4006/week @ 2024-05-11 3825/week @ 2024-05-18 2840/week @ 2024-05-25 3966/week @ 2024-06-01 3707/week @ 2024-06-08 4300/week @ 2024-06-15 4928/week @ 2024-06-22 4085/week @ 2024-06-29 4600/week @ 2024-07-06 4814/week @ 2024-07-13 4643/week @ 2024-07-20 5395/week @ 2024-07-27 5376/week @ 2024-08-03

每月 20,788 次下载
36 crate 中使用 (直接使用 34 个)

MIT 许可证

53KB
785

rexpect

crates.io Released API docs Master API docs [MSRV]

启动、控制和响应子应用程序和进程的预期模式,从而实现交互自动化和测试。组件包括

  • session:启动新进程并与它交互;rexpect 的主要模块。
  • reader:非阻塞读取器,支持等待字符串、正则表达式和 EOF。
  • process:在 pty 中启动进程。

目标是提供与 pexpect 相似的功能集。

示例

更多示例,请查看示例目录。

基本用法

将此添加到您的 Cargo.toml

[dependencies]
rexpect = "0.4"

通过 ftp 交互的简单示例

extern crate rexpect;

use rexpect::spawn;
use rexpect::errors::*;

fn do_ftp() -> Result<()> {
    let mut p = spawn("ftp speedtest.tele2.net", Some(30_000))?;
    p.exp_regex("Name \\(.*\\):")?;
    p.send_line("anonymous")?;
    p.exp_string("Password")?;
    p.send_line("test")?;
    p.exp_string("ftp>")?;
    p.send_line("cd upload")?;
    p.exp_string("successfully changed.\r\nftp>")?;
    p.send_line("pwd")?;
    p.exp_regex("[0-9]+ \"/upload\"")?;
    p.send_line("exit")?;
    p.exp_eof()?;
    Ok(())
}

fn main() {
    do_ftp().unwrap_or_else(|e| panic!("ftp job failed with {}", e));
}

与 bash 和读取程序结合的示例

extern crate rexpect;
use rexpect::spawn_bash;
use rexpect::errors::*;

fn do_bash() -> Result<()> {
    let mut p = spawn_bash(Some(2000))?;

    // case 1: wait until program is done
    p.send_line("hostname")?;
    let hostname = p.read_line()?;
    p.wait_for_prompt()?; // go sure `hostname` is really done
    println!("Current hostname: {}", hostname);

    // case 2: wait until done, only extract a few infos
    p.send_line("wc /etc/passwd")?;
    // `exp_regex` returns both string-before-match and match itself, discard first
    let (_, lines) = p.exp_regex("[0-9]+")?;
    let (_, words) = p.exp_regex("[0-9]+")?;
    let (_, bytes) = p.exp_regex("[0-9]+")?;
    p.wait_for_prompt()?; // go sure `wc` is really done
    println!("/etc/passwd has {} lines, {} words, {} chars", lines, words, bytes);

    // case 3: read while program is still executing
    p.execute("ping 8.8.8.8", "bytes of data")?; // returns when it sees "bytes of data" in output
    for _ in 0..5 {
        // times out if one ping takes longer than 2s
        let (_, duration) = p.exp_regex("[0-9. ]+ ms")?;
        println!("Roundtrip time: {}", duration);
    }
    p.send_control('c')?;
    Ok(())
}

fn main() {
    do_bash().unwrap_or_else(|e| panic!("bash job failed with {}", e));
}

与 bash 和作业控制结合的示例

发送 ctrl-c 和类似操作时,一个常见的问题是需要确保程序已完全加载,否则 ctrl-* 将进入虚无之地。有两个函数可以确保这一点

  • execute 其中您需要提供一个匹配字符串,当程序准备好时该字符串出现在 stdout/stderr 上
  • wait_for_prompt 它等待提示再次出现
extern crate rexpect;
use rexpect::spawn_bash;
use rexpect::errors::*;

fn do_bash_jobcontrol() -> Result<()> {
    let mut p = spawn_bash(Some(1000))?;
    p.execute("ping 8.8.8.8", "bytes of data")?;
    p.send_control('z')?;
    p.wait_for_prompt()?;
    // bash writes 'ping 8.8.8.8' to stdout again to state which job was put into background
    p.execute("bg", "ping 8.8.8.8")?;
    p.wait_for_prompt()?;
    p.send_line("sleep 0.5")?;
    p.wait_for_prompt()?;
    // bash writes 'ping 8.8.8.8' to stdout again to state which job was put into foreground
    p.execute("fg", "ping 8.8.8.8")?;
    p.send_control('c')?;
    p.exp_string("packet loss")?;
    Ok(())
}

fn main() {
    do_bash_jobcontrol().unwrap_or_else(|e| panic!("bash with job control failed with {}", e));
}

项目状态

Rexpect 涵盖了 pexpect 的几乎所有功能。如果您缺少任何功能,我非常乐意接收 PR 或 Issue 请求。

测试涵盖了大多数方面,并且应该可以在 Linux 或 Mac 上针对 rust stable、beta 和 nightly 运行。

设计决策

  • 使用 error-chain 的错误处理
  • 使用 nix(尽可能避免 libc)以保持代码的安全和整洁
  • 遗憾的是,在Rust中,expect 被过度使用来解包 OptionResult,请使用 exp_* 代替

许可协议:MIT 许可协议

依赖项

~5–15MB
~209K SLoC