Crates.io | rexpect |
lib.rs | rexpect |
version | 0.6.0 |
source | src |
created_at | 2017-07-27 04:48:55.684566 |
updated_at | 2024-11-06 17:11:54.82303 |
description | Interact with unix processes/bash the same way as pexpect or Don libes expect does |
homepage | |
repository | https://github.com/rust-cli/rexpect |
max_upload_size | |
id | 25242 |
size | 92,568 |
Spawn, control, and respond to expected patterns of child applications and processes, enabling the automation of interactions and testing. Components include:
The goal is to offer a similar set of functionality as pexpect.
For more examples, check the examples directory.
Simple example for interacting via ftp:
use rexpect::spawn;
use rexpect::error::*;
fn do_ftp() -> Result<(), Error> {
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));
}
use rexpect::spawn_bash;
use rexpect::error::*;
fn do_bash() -> Result<(), Error> {
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));
}
One frequent bitfall with sending ctrl-c and friends is that you need to somehow ensure that the program has fully loaded, otherwise the ctrl-* goes into nirvana. There are two functions to ensure that:
execute
where you need to provide a match string which is present
on stdout/stderr when the program is ready
wait_for_prompt
which waits until the prompt is shown again
use rexpect::spawn_bash;
use rexpect::error::*;
fn do_bash_jobcontrol() -> Result<(), Error> {
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));
}
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.