Я делаю небольшое приложение ncurses в Rust, которое должно связываться с дочерним процессом. У меня уже есть прототип, написанный в Common Lisp; gif here, надеюсь, покажет, что я хочу сделать. Я пытаюсь переписать его, потому что CL использует огромный объем памяти для такого маленького инструмента.Как читать выходные данные дочернего процесса без блокировки в Rust?
Я не использовал Rust раньше (или других языков низкого уровня), и у меня возникли проблемы с выяснением того, как взаимодействовать с подпроцессом.
То, что я сейчас делаю примерно так:
Создать процесс:
let mut program = match Command::new(command) .args(arguments) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() { Ok(child) => child, Err(_) => { println!("Cannot run program '{}'.", command); return; }, };
передать его бесконечным (до выходов пользователя) цикла, который считывает и обрабатывает входные и ожидает выхода, как это (и записывает его на экран):
fn listen_for_output(program: &mut Child, output_viewer: &TextViewer) { match program.stdout { Some(ref mut out) => { let mut buf_string = String::new(); match out.read_to_string(&mut buf_string) { Ok(_) => output_viewer.append_string(buf_string), Err(_) => return, }; }, None => return, }; }
Однако вызов read_to_string
блокирует программу, пока процесс не завершится. Из того, что я вижу, read_to_end
и read
также, похоже, блокируются. Если я попытаюсь запустить что-то вроде ls
, которое сразу же выходит, оно работает, но с чем-то, что не выходит, как python
или sbcl
, оно продолжается только после того, как я убью подпроцесс вручную.
Edit:
на основе this answer, я изменил код, чтобы использовать BufReader
:
fn listen_for_output(program: &mut Child,
output_viewer: &TextViewer) {
match program.stdout.as_mut() {
Some(out) => {
let buf_reader = BufReader::new(out);
for line in buf_reader.lines() {
match line {
Ok(l) => {
output_viewer.append_string(l);
},
Err(_) => return,
};
}
},
None => return,
}
}
Однако проблема все еще остается тем же самым. Он будет читать все доступные строки, а затем блокировать. Так как инструмент должен работать с любой программой, нет способа угадать, когда выход закончится, прежде чем пытаться прочитать. Кажется, что не существует способа установить тайм-аут для BufReader
.
Спасибо за полезное объяснение. Я посмотрю MIO, и если это не сработает, я буду использовать отдельные потоки. – jkiiski