2015-07-23 2 views
7

Ich möchte in der Lage sein, Rust zu verwenden, um eine Child-Shell zu erzeugen, dann wiederhole ich sie willkürlich und verarbeite ihre Ausgaben. Ich habe viele Beispiele online gefunden, die mir zeigen, wie man einen einzelnen Befehl übergibt und seine einzige Ausgabe erhält, aber ich kann es nicht immer wieder tun.Konnte nicht mehr als einmal in den oder aus dem erzeugten Child-Prozess pipen

Zum Beispiel hängt der folgende Code in der Zeile nach dem Kommentar. (Ich stelle mir vielleicht read_to_string() blockiert wird, bis er von dem Kind Prozess stdout erhält, aber wenn so verstehe ich nicht, warum das Ausgangs ausbleibt ..)

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), 
     } 
    } 
} 

ich ein Anfänger in Rust bin und ich Ich denke, das Problem ist mein begrenztes Verständnis der borrow-checker/referenzierenden Regeln, da das obige gut läuft (für eine einzige Iteration), wenn ich die Schleifenanweisung aus dem Code entferne und die Referenzen auf die Innereien der std::process::Child struct in unveränderlich ändere; zum Beispiel aus dieser:

child_shell.stdin.as_mut().unwrap().write("ls".as_bytes()) 

dazu:

child_shell.stdin.unwrap().write("ls".as_bytes()) 

Offensichtlich wiederholt ausgeführt ls nicht mein Ziel ist es, und ich weiß, dass ich nur ein Shell-Skript schreiben könnte und dann haben Rust wiederholt mach es - aber (abgesehen von dem Ziel, nur mehr über Rust zu lernen!) muss ich zumindest prinzipiell für ein komplizierteres Projekt tun können (auf das ich mich gerne einlasse, wenn es möglich ist) sich für irgendwelche Lösungen als relevant erweisen, aber es ist wahrscheinlich, Weg, weit außerhalb des Umfangs dieser Frage!)

Schließlich, wenn es sich herausstellt, dass es nicht möglich ist, eine Kind-Shell auf diese Weise zu verwenden, würde ich dennoch gerne lernen, wie man wiederholt und kontinuierlich einen und einen anderen beliebigen Befehl ausführt, wie ich konnte keine Informationen in der Rust Dokumentation, Tutorials oder auf Stack Overflow finden.

Antwort

7

read_to_string wird dokumentiert, wie

Alle Bytes bis EOF in dieser Quelle

So ist es zu warten, bis alle Eingaben erfolgt, was nie passieren wird, bis die Schale geschlossen ist. Sie können dies beheben, indem Sie eine bestimmte Menge von Daten aus der Ausgabe lesen. Hier ist ein Beispiel, wo ich all die netten Fehler Druck entfernt man den Kern der Lösung zeigen musste:

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); 
    } 
} 

Hier verwenden wir das BufRead Merkmal von dem Eingang zu erlauben Lesen, bis wir eine Zeile wert gelesen zu haben. Dann drucken wir das aus und fahren mit unserer Schleife fort. Natürlich gibt es mehr als eine Zeile der Ausgabe pro Zeile der Eingabe, so wird dies nur mehr und mehr warten, um gelesen zu werden.

In Ihrem echten Code müssen Sie herausfinden müssen, wann Sie aufhören zu lesen. Dies kann sehr einfach sein, wenn Sie Antworten mit fester Größe haben oder sehr schwierig, wenn Sie versuchen, mit einem interaktiven Programm zu arbeiten.

Vorsicht bei der Verwendung von child_shell.stdin oder stdout direkt, ohne as_ref/as_mut. Wenn Sie diese direkt verwenden, wird dieser Artikel aus der Struktur Child entfernt, wobei die Child teilweise gültig bleibt. Sie könnten beispielsweise nicht mehr wait oder kill aufrufen.

auf einem nicht verwandten Notiz, Sie müssen Charakterzug Methoden nicht aufrufen wie Error::description(&why). Sie können einfach why.description() sagen.

+0

Vielen Dank! Alle wirklich nützlichen Ratschläge und Beobachtungen. Im Hinblick darauf, herauszufinden, wann das Lesen aus dem 'BufReader' gestoppt werden sollte, warum versucht man einfach, child_out.lines(). Count() 'oder' for l in child_out.lines() 'innerhalb der Schleife aufzurufen gibt mir eine 'Verwendung von verschobenem Wert: child_out' Fehler? Warum kann ich den "BufReader" für eine Zeile fragen, aber nicht für alle? Mißverstehe ich, wie Iteratoren hier arbeiten? – John

+0

@John nicht wie Iteratoren arbeiten, aber wie der 'self' Parameter bei diesen Methoden funktioniert. Check out ['count'] (http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count) um zu sehen, dass es" self "nach Wert nimmt, was den zugrundeliegenden Iterator verbraucht , wie [Zeilen] (http://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines). Der Aufruf von 'count' würde sowieso keinen Sinn ergeben, da man * all * der Prozessausgabe lesen müsste, um die Zählung zu erhalten, in der alle Daten weggeworfen würden. – Shepmaster

Verwandte Themen