Если вам когда-либо приходилось передавать большой объем данных из какой-либо программы foo в другую программу bar, вы, наверное, знакомы с просмотрщик труб заявление. Если вы не знакомы с приложением, вот небольшая демонстрация минимальной перезаписи:
По сути, pv примет любой вклад от stdin (или файлы) и отправьте его stdout показывая прогресс по stderr. Обратите внимание, что оригинал pv имеет еще несколько функций, которые не были целями этого проекта.
Cargo.toml (зависимости)
[dependencies]
anyhow = "1.0"
indicatif = "0.15.0"
structopt = "0.3"
main.rs
use anyhow::Result;
use indicatif::{ProgressBar, ProgressStyle};
use std::fs::File;
use std::io::{self, ErrorKind, Read};
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "pipeviewer", about = "A pipe inspecting application.")]
struct Opt {
/// Input file, stdin if not specified
#[structopt(parse(from_os_str))]
input: Option<PathBuf>,
}
fn main() -> Result<()> {
let opts = Opt::from_args();
let (mut input, len): (Box<dyn Read>, Option<u64>) = if let Some(file) = opts.input {
let file = File::open(file)?;
let len = file.metadata()?.len();
(Box::new(file), Some(len))
} else {
(Box::new(io::stdin()), None)
};
let pb = if let Some(len) = len {
let pb = ProgressBar::new(len);
pb.set_style(ProgressStyle::default_bar().template(
"[{elapsed_precise}] {bar} {bytes_per_sec} [{bytes}/{total_bytes}] ETA: {eta}",
));
pb
} else {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::default_spinner()
.template("[{elapsed_precise}] {spinner} {bytes_per_sec} [{bytes}]"),
);
pb
};
let mut output = pb.wrap_write(io::stdout());
match io::copy(&mut input, &mut output) {
Ok(_) => Ok(()),
Err(e) if e.kind() == ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e.into()),
}
}
К сожалению, в коде немного преобладает стиль индикатора выполнения. Однако я не хотел показывать бессмысленную полосу выполнения для неизвестного количества данных (например, foo | pv | bar), и я не хотел показывать никакого прогресса если объем данных известен (например, pv somefile).
Меня больше всего интересуют вопросы стиля, хитрости и подобные грехи Rust. Я знаю, что могу добавить некоторые дополнительные функции (например, больше файлов в качестве аргументов, подсказки размера для STDIN, поддержку нескольких pvв одной трубе) и с радостью черпаю вдохновение из обзоров, но в первую очередь я хочу улучшить свои навыки работы с Rust :).
Некоторые дополнительные замечания
Этот раздел дает лишь некоторую предысторию и его совершенно необязательно читать :).
Мотивация
Это переписывание вдохновлено Удеми конечно Практическое системное программирование на Rust. Однако я сильно отклоняюсь от первоначального курса, поскольку он вводит многопоточность (через crossbeam) и другие (слегка) лишние функции.
Цели lazy pipeviewer rewrite
- по возможности используйте уже существующие ящики (не наезжай Национальные институты здравоохранения США)
- взять либо одно имя файла, либо использовать stdin в качестве ввода
- промыть все в стандартный вывод (но не обращать внимания на сломанные трубы)
- не переусердствуйте, например
- нет многопоточности, если одноядерная производительность достаточно высока
- нет
BufReaderили жеBufWriterесли небуферизованные варианты кажутся достаточно быстрыми - нет явной буферизации через
Read::read(&mut [u8])если есть возможность, ПОЦЕЛУЙ!
Приглушить это
При написании этого ленивого кода был момент, когда while let Ok(n) = input.read(&mut buf) был введен, но с учетом этого io::copy показалось достаточно быстрым, я не стал использовать его в финальной версии. То же самое касается BufReader и BufWriter, так как они не улучшили поведение на моих машинах. Могут потребоваться дополнительные тесты, но это может более или менее победить ленивую часть;).
Знакомство с ящиками (для других проектов)
Весь этот проект более или менее предназначен как упражнение для а) поиска подходящих ящиков и б) их правильного использования. Разбор аргумента? Почти всегда необходимо. Правильная обработка ошибок? Обязательным. Показываете красивый индикатор выполнения на пути к завершению? Большой бонус!
Зависимости
Что касается зависимостей, я выбрал

