Tôi không thấy gì trong thư viện chuẩn thực hiện điều này cho bạn. Không có nghĩa là bạn không thể tự viết nó. Điều này cũng có nghĩa là bạn có thể quyết định tần suất mỗi bộ mô tả tệp được đọc và cách kết hợp dữ liệu từ mỗi bộ mô tả tệp. Ở đây, tôi cố gắng đọc theo các khối bằng cách sử dụng kích thước mặc định là BufReader
và muốn đặt dữ liệu stdout đầu tiên khi cả hai trình mô tả có dữ liệu.
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))
}
Khoảng cách lớn nhất là khi quá trình đã thoát. Tôi ngạc nhiên vì thiếu một phương pháp liên quan trên Child
.
Xem thêm How do I prefix Command stdout with [stdout] and [sterr]?
Trong giải pháp này, không có bất kỳ trật tự nội tại giữa các file descriptor. Tương tự, hãy tưởng tượng hai xô nước. Nếu bạn bỏ trống một thùng và sau đó thấy rằng nó đã được lấp đầy lần nữa, bạn biết xô thứ hai đến sau lần đầu tiên. Tuy nhiên, nếu bạn làm trống hai nhóm và quay lại sau và cả hai đều được lấp đầy, bạn không thể cho biết nhóm nào đã được điền trước.
"Chất lượng" xen kẽ là vấn đề tần suất bạn đọc từ mỗi bộ mô tả tệp và bộ mô tả tệp nào được đọc trước tiên. Nếu bạn đọc một byte đơn từ mỗi vòng trong một vòng lặp rất chặt chẽ, bạn có thể nhận được kết quả hoàn toàn bị cắt xén nhưng đây sẽ là "chính xác" nhất liên quan đến đặt hàng. Tương tự như vậy, nếu một chương trình in "A" để stderr sau đó "B" để stdout nhưng vỏ đọc từ stdout trước stderr, sau đó kết quả sẽ là "BA", trông ngược.
Tôi đang tìm kiếm thứ gì đó như 'let pipe2 = Stdio :: from_raw_fd (pipe.to_raw_fd())', nhưng tôi không thể thấy việc triển khai 'AsRawFd' trong tài liệu, khó chịu. –
Tôi không chắc chắn nếu 'Stdio :: piped()' là những gì bạn cần, nhưng tại sao không chỉ sử dụng '.stdout (Stdio :: piped()). Stderr (Stdio :: piped())'? [RustByExample] (http://rustbyexample.com/std_misc/process/pipe.html) thực hiện việc này. – Manishearth
@Manishearth Điều đó nghe có vẻ như nó sẽ chuyển hướng stdin và stdout đến hai * riêng biệt * ống.OP có thể muốn chuyển hướng chúng đến cùng một đường ống mà không làm mất thông tin đặt hàng giữa các nội dung của chúng. Ví dụ, cấu trúc shell Bourne tương đương, 'output = $ (command 2> & 1)'. – user4815162342