Невозможно передать в или из порожденного дочернего процесса более одного раза
Я хочу иметь возможность использовать Rust для порождения дочерней оболочки, затем многократно передавать ей произвольные команды и обрабатывать их результаты. Я нашел много примеров в Интернете, показывающих мне, как передать одну команду и получить ее единственный вывод, но я не могу повторить это.
Например, следующий код висит на строке после комментария. (Я думаю, может быть, read_to_string()
блокируется до тех пор, пока не получит стандартный вывод от дочернего процесса, но если это так, я не понимаю, почему этот вывод не ожидается..)
let mut child_shell = match Command::new("/bin/bash")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Err(why) => panic!("couldn't spawn child_shell: {}", Error::description(&why)),
Ok(process) => process,
};
loop {
{
match child_shell.stdin.as_mut().unwrap().write("ls".as_bytes()) {
Err(why) => panic!(
"couldn't send command to child shell: {}",
Error::description(&why)
),
Ok(_) => println!("sent command to child shell"),
}
}
{
let mut s = String::new();
// ↓ hangs on this line ↓
match child_shell.stdout.as_mut().unwrap().read_to_string(&mut s) {
Err(why) => panic!("couldn't read bash stdout: {}", Error::description(&why)),
Ok(_) => print!("bash responded with:\n{}", s),
}
}
}
Я новичок в Rust, и я думаю, что проблема заключается в моем ограниченном понимании правил заимствования / ссылок, поскольку вышеприведенное работает нормально (для одной итерации), если я удаляю инструкцию цикла из кода и изменяю ссылки на внутренности std::process::Child
структура неизменной; например из этого:
child_shell.stdin.as_mut().unwrap().write("ls".as_bytes())
к этому:
child_shell.stdin.unwrap().write("ls".as_bytes())
Очевидно, многократно работает ls
не моя конечная цель, и я знаю, что я мог бы просто написать сценарий оболочки, а затем запустить Rust несколько раз, но (кроме цели просто узнать больше о Rust!), это то, что мне нужно делать По крайней мере, в принципе, для более сложного проекта (который я с радостью расскажу, если он окажется актуальным для каких-либо решений, но, возможно, это выходит далеко за рамки этого вопроса!)
Наконец, если выяснится, что таким способом невозможно использовать дочернюю оболочку, я, тем не менее, хотел бы узнать, как многократно / непрерывно передавать данные в и из порожденного процесса, выполняющего какую-либо другую произвольную команду, как я это не делал. не может найти какую-либо информацию в документации, руководствах по Rust или в переполнении стека.
1 ответ
read_to_string
задокументировано как
Прочитать все байты до EOF в этом источнике
Таким образом, он ожидает, пока весь ввод не будет выполнен, что никогда не произойдет, пока оболочка не будет закрыта. Вы можете исправить это, прочитав определенное количество данных из вывода. Вот пример, где я удалил все приятные ошибки печати, которые вы должны были показать суть решения:
use std::process::{Command, Stdio};
use std::io::{BufRead, Write, BufReader};
fn main() {
let mut child_shell = Command::new("/bin/bash")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
let child_in = child_shell.stdin.as_mut().unwrap();
let mut child_out = BufReader::new(child_shell.stdout.as_mut().unwrap());
let mut line = String::new();
loop {
child_in.write("ls\n".as_bytes()).unwrap();
child_out.read_line(&mut line).unwrap();
println!("{}", line);
}
}
Здесь мы используем BufRead
черта, позволяющая читать из ввода, пока мы не прочитаем одну строку. Затем мы распечатываем это и продолжаем наш цикл. Конечно, в каждой строке ввода имеется более одной строки вывода, так что просто будет все больше и больше ожидающих чтения.
В вашем реальном коде вам нужно будет выяснить, когда прекратить чтение. Это может быть очень просто, если у вас есть ответы фиксированного размера, или очень сложно, если вы пытаетесь справиться с интерактивной программой.
Будьте осторожны с использованием child_shell.stdin
или же stdout
напрямую, без as_ref
/ as_mut
, Их прямое использование переместит этот элемент из Child
структура, оставляя Child
частично действует. Вы больше не сможете звонить wait
или же kill
на это например.
На несвязанной ноте вам не нужно вызывать такие методы, как Error::description(&why)
, Вы можете просто сказать why.description()
,