Ich sehe nichts in der Standardbibliothek, die das für Sie tut. Bedeutet nicht, dass du es nicht selbst schreiben kannst. Dies bedeutet auch, dass Sie entscheiden müssen, wie häufig jeder Dateideskriptor gelesen wird und wie die Daten von jedem der Dateideskriptoren kombiniert werden. Hier versuche ich, Chunks mit der Standardgröße BufReader
einzulesen und bevorzuge es, stdout-Daten zuerst zu setzen, wenn beide Deskriptoren Daten haben.
use std::io::prelude::*;
use std::io::BufReader;
use std::process::{Command, Stdio};
fn main() {
let mut child =
Command::new("/tmp/output")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Couldn't run program");
let mut output = Vec::new();
// Should be moved to a function that accepts something implementing `Write`
{
let stdout = child.stdout.as_mut().expect("Wasn't stdout");
let stderr = child.stderr.as_mut().expect("Wasn't stderr");
let mut stdout = BufReader::new(stdout);
let mut stderr = BufReader::new(stderr);
loop {
let (stdout_bytes, stderr_bytes) = match (stdout.fill_buf(), stderr.fill_buf()) {
(Ok(stdout), Ok(stderr)) => {
output.write_all(stdout).expect("Couldn't write");
output.write_all(stderr).expect("Couldn't write");
(stdout.len(), stderr.len())
}
other => panic!("Some better error handling here... {:?}", other)
};
if stdout_bytes == 0 && stderr_bytes == 0 {
// Seems less-than-ideal; should be some way of
// telling if the child has actually exited vs just
// not outputting anything.
break;
}
stdout.consume(stdout_bytes);
stderr.consume(stderr_bytes);
}
}
let status = child.wait().expect("Waiting for child failed");
println!("Finished with status {:?}", status);
println!("Combined output: {:?}", std::str::from_utf8(&output))
}
Die größte Lücke ist, wenn der Prozess beendet wurde. Ich bin überrascht über das Fehlen einer relevanten Methode auf Child
.
auch bei dieser Lösung How do I prefix Command stdout with [stdout] and [sterr]?
See, gibt es keine intrinsische Ordnung zwischen den Datei-Deskriptoren. Stellen Sie sich als Analogie zwei Eimer Wasser vor. Wenn Sie einen Eimer leeren und später sehen, dass er wieder gefüllt ist, wissen Sie, dass der zweite Eimer nach dem ersten gekommen ist. Wenn Sie jedoch zwei Eimer leeren und später wiederkommen und beide gefüllt sind, können Sie nicht erkennen, welcher Eimer zuerst gefüllt wurde.
Die "Qualität" des Interleavings hängt davon ab, wie häufig Sie von jedem Dateideskriptor lesen und welcher Dateideskriptor zuerst gelesen wird. Wenn Sie in einer sehr engen Schleife ein einzelnes Byte aus jedem lesen, könnten Sie völlig verzerrte Ergebnisse erhalten, aber diese wären hinsichtlich der Reihenfolge am "genauesten". Wenn ein Programm "A" in stderr ausgibt, dann "B" in stdout, aber die Shell liest von stdout vor stderr, dann wäre das Ergebnis "BA", das rückwärts schaut.
Ich war für so etwas wie 'lassen PIPE2 = Stdio :: from_raw_fd (pipe.to_raw_fd())' suchen, aber ich kann nicht eine Implementierung von 'AsRawFd' in der Dokumentation, annoyingly sehen. –
Ich bin nicht sicher, ob 'Stdio :: piped()' das ist, was Sie brauchen, aber warum nicht einfach '.stdout (Stdio :: piped()) verwenden. Stderr (Stdio :: piped())'? [RustByExample] (http://rustbyexample.com/std_misc/process/pipe.html) tut dies. – Manishearth
@Manishearth Das klingt, als würde es stdin und stdout auf zwei * separate * rohre umleiten.Das OP möchte sie vielleicht auf die gleiche Leitung umleiten, ohne die Bestellinformationen zwischen ihren Inhalten zu verlieren. Das entsprechende Bourne-Shell-Konstrukt ist zum Beispiel "output = $ (command 2> & 1)". – user4815162342