The goal is to offer a similar set of functionality as pexpect.
Add this to your Cargo.toml
toml
[dependencies]
rexpect = "0.3"
Simple example for interacting via ftp:
```rust extern crate rexpect;
use rexpect::spawn; use rexpect::errors::*;
fn doftp() -> Result<()> { let mut p = spawn("ftp speedtest.tele2.net", Some(30000))?; p.expregex("Name \(.*\):")?; p.sendline("anonymous")?; p.expstring("Password")?; p.sendline("test")?; p.expstring("ftp>")?; p.sendline("cd upload")?; p.expstring("successfully changed.\r\nftp>")?; p.sendline("pwd")?; p.expregex("[0-9]+ \"/upload\"")?; p.sendline("exit")?; p.exp_eof()?; Ok(()) }
fn main() { doftp().unwrapor_else(|e| panic!("ftp job failed with {}", e)); } ```
```rust
extern crate rexpect; use rexpect::spawn_bash; use rexpect::errors::*;
fn foo() -> 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')?;
} ```
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 readywait_for_prompt
which waits until the prompt is shown again```rust extern crate rexpect; use rexpect::spawn_bash; use rexpect::errors::*;
fn run() -> Result<()> { let mut p = spawnbash(Some(1000))?; p.execute("ping 8.8.8.8", "bytes of data")?; p.sendcontrol('z')?; p.waitforprompt()?; // 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.waitforprompt()?; p.sendline("sleep 1")?; p.waitforprompt()?; // 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.sendcontrol('c')?; p.exp_string("packet loss")?; Ok(()) }
```
Rexpect covers more or less the features of pexpect. If you miss anything I'm happy to receive PRs or also Issue requests of course.
The tests cover most of the aspects and it should run out of the box for rust stable, beta and nightly on both Linux or Mac.
That said, I don't know of too many people using it yet, so use this with caution.
expect
is used in rust too prominently to unwrap Option
s and Result
s, use exp_*
instead