我正在尝试使用 Rust 中进程的输出。如果进程在一定时间后没有终止,我想终止/杀死它。理想情况下,我想将所有内容包装在生成器中,以便我可以逐行迭代输出,但我在 Rust 中还没有足够的经验。
这是我的代码(src/main.rs
(,使用子进程箱:
use subprocess::{Popen, PopenConfig, Redirection};
use std::io::Read;
use std::time::Duration;
fn main() {
let mut p = Popen::create(&["bash", "-c", "echo "Hi There!"; sleep 1000"], PopenConfig {stdout: Redirection::Pipe, ..Default::default()}, ).unwrap();
let mut output = p.stdout.take().unwrap();
let thread = std::thread::spawn(move || {
let three_secs = Duration::from_secs(3);
let one_sec = Duration::from_secs(1);
let r = p.wait_timeout(three_secs).unwrap();
match r {
None => {
println!("Wait timed out, terminating process");
p.terminate().unwrap();
let r = p.wait_timeout(one_sec).unwrap();
match r {
None => {
println!("Termination didn't seem to work, killing");
p.kill().unwrap();
},
Some(_) => {
println!("Terminated process successfully");
},
}
p.wait().unwrap();},
Some(_) => { println!("Process returned");},
}
println!("Everything fine");
});
println!("Starting to read output");
let mut output_str = String::new();
output.read_to_string(&mut output_str).unwrap();
println!("Done reading output");
thread.join().unwrap();
println!("Output: {}", output_str);
println!("Hello, world!");
}
我希望得到以下输出:
Starting to read output
Wait timed out, terminating process
Terminated process successfully
Everything fine
Done reading output
Output: Hi There!
Hello, world!
三秒钟后该过程完成。但我得到的是
Starting to read output
Wait timed out, terminating process
Terminated process successfully
Everything fine
并且该过程不会终止。
为了完整起见,以下是我对上述src/main.rs
的Cargo.toml
:
[package]
name = "subproc"
version = "0.1.0"
authors = ["<snip>"]
edition = "2018"
[dependencies]
subprocess = "0.2.4"
我会找一个板条箱来帮助你做到这一点。
也许是这样的: https://docs.rs/wait-timeout/0.2.0/wait_timeout/
下面是适用于捕获 stdout 并逐行迭代的示例代码:
use std::io::Read;
use std::process::{Command, Stdio};
use std::time::Duration;
use wait_timeout::ChildExt;
fn main() {
let mut child = Command::new("sh")
.arg("-c")
.arg("while true; do date; sleep 1; done")
.stdout(Stdio::piped())
.spawn()
.unwrap();
let secs = Duration::from_secs(5);
let _status_code = match child.wait_timeout(secs).unwrap() {
Some(status) => status.code(),
None => {
child.kill().unwrap();
child.wait().unwrap().code()
}
};
let mut s = String::new();
child.stdout.unwrap().read_to_string(&mut s).unwrap();
for (num, line) in s.split("n").enumerate() {
println!("{}: {}", num, line);
}
}
指纹:
0: Mon Jun 1 14:42:06 BST 2020
1: Mon Jun 1 14:42:07 BST 2020
2: Mon Jun 1 14:42:08 BST 2020
3: Mon Jun 1 14:42:09 BST 2020
4: Mon Jun 1 14:42:10 BST 2020
5:
如果你想在孩子运行时做其他工作,你必须使用事件循环或线程。
在os_pipe
库的作者杰克·奥康纳(Jack O'Connor(的帮助下,我设法编写了一个解决方案,该解决方案将读取进程输出,并在另一个线程中进行超时等待和终止。请注意,此解决方案只会杀死启动的进程,而不会杀死其子进程,如果您的子进程本身具有子进程,则需要更多处理。
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader};
use std::thread;
use std::time;
fn main() {
const TIMEOUT : i32 = 5;
let mut cmd = Command::new("bash");
cmd.arg("-c").arg("for ((i=1000; i > 0; i--)); do echo "$i bottles of beer on the wall"; sleep 1; done");
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn().unwrap();
let stdout = child.stdout.take().unwrap();
let thread = thread::spawn(move || {
for _ in 0..TIMEOUT {
if let Ok(Some(_)) = child.try_wait() {
return;
}
thread::sleep(time::Duration::from_secs(1));
}
child.kill().unwrap();
});
let reader = BufReader::new(stdout);
for line in reader.lines() {
println!("line: {}", line.unwrap());
}
thread.join().unwrap();
println!("Hello, world!");
}