I'm using Tokio's process::Command to run a command locally. When I use spawn, I know I get back a Child and from that Child, I can get ChildStdout and ChildStderr. How can I continuously poll to grab the latest output while waiting for the child to finish?
You can keep reading from the ChildStdout until you get a read of size 0, then call wait on the child. The child is guaranteed to close its stdout and stderr when it exits (but may also close them before exiting).
Note also that the stdout/stderr objects can be taken out of the Child object, allowing you to access both the pipes and the Child object simultaneously.
I'm really new to async in Rust and trying to get my head around it (mostly by trying to forget my knowledge of async Python which does things a bit differently). Am I on the right path with something like this:
let child = match command.spawn() {
Ok(child) => child,
Err(err) => {
slog::error!(logging::get(), "Failed to spawn child process: {:?}", err);
return Err(anyhow!(err));
}
};
let stdout = match child.stdout.take() {
Some(stdout) => stdout,
None => {
slog::error!(logging::get(), "Failed to get child stdout stream",);
return Err(anyhow!("Failed to get child stdout stream"));
}
};
let stderr = match child.stderr.take() {
Some(stderr) => stderr,
None => {
slog::error!(logging::get(), "Failed to get child stderr stream",);
return Err(anyhow!("Failed to get child stderr stream"));
}
};
loop {
select! {
stdo = stdout => println!(stdo),
stde = stderr => eprintln!(stde),
output = child => println!("Done!"),
complete => break,
};
}
One more question: do you know if reading out of the buffer will remove the things I read from the buffer?
Some context on what I'm doing: sometimes the command can have tons and tons of output, and to keep the memory footprint small, i want to read this as the process runs so I can keep it trimmed down in a Vec:
let mut line = String::new();
let mut lines: Vec<u8> = Vec::new();
loop {
line.clear();
match stdout_reader.read_line(&mut line).await {
[...]
Ok(_) => {
lines.extend_from_slice(line.as_bytes());
// keep only the last 512K bytes
let length = lines.len();
if length > 51200 {
lines.drain(..length - 51200).for_each(drop);
}
}
}
}
What I meant was will I be freeing system resources (memory in this case) by reading out of the buffer and draining it and trimming to the size I need. The only reason I'm doing this live reading is because sometimes a command can output gigabytes of data and I don't want to OOM the machine, so I want to read and throw away everything but hold on to the last 512K of the output. In other languages, I noticed that while I could certainly stream the output, the stdio/stderr output was still held on to by the internals of the implementation so it didn't really help with resource management.