1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
//! This library lets you execute a child process and catch its output (stdout and stderr). //! This is useful if you want to use the output from a specific command and transform it //! in your program. //! //! ⚠️ Difference to std::process::Command 🚨 //! `std::process::Command` does the same in the standard library but **with one exception**: //! My library gives you access to `stdout`, `stderr`, **and** `"stdcombined"`. This way you get all //! output lines in the order they appeared. That's the unique feature of this crate. use std::rc::Rc; use derive_more::Display; #[macro_use] extern crate log; mod error; mod pipe; mod libc_util; mod exec; mod child; mod reader; pub use exec::fork_exec_and_catch; /// Holds the information from the executed process. It depends on the `strategy` option of /// [`crate::fork_exec_and_catch`] how the output is structured. /// /// The strategy results in the following two kinds of outputs: /// * `stdout_lines` and `stderr_lines` are correct but `stdcombined_lines` is only /// maybe in correct order /// * or `stdout_lines` and `stderr_lines` are `None`, but `stdcombined_lines` is in correct order #[derive(Debug)] pub struct ProcessOutput { /// Exit code of the process. 0 is success, >1 is error. /// See https://man7.org/linux/man-pages/man3/errno.3.html exit_code: i32, /// * `None` for [`crate::OCatchStrategy::StdCombined`] /// * `Some` for [`crate::OCatchStrategy::StdSeparately`] stdout_lines: Option<Vec<Rc<String>>>, /// * `None` for [`crate::OCatchStrategy::StdCombined`] /// * `Some` for [`crate::OCatchStrategy::StdSeparately`] stderr_lines: Option<Vec<Rc<String>>>, /// * All output lines in correct order for [`crate::OCatchStrategy::StdCombined`] /// * All output lines in not guaranteed correct order for [`crate::OCatchStrategy::StdSeparately`] stdcombined_lines: Vec<Rc<String>>, /// The strategy that was used. See [`crate::OCatchStrategy::StdSeparately`]. strategy: OCatchStrategy, } impl ProcessOutput { /// Constructor. fn new(stdout_lines: Option<Vec<Rc<String>>>, stderr_lines: Option<Vec<Rc<String>>>, stdcombined_lines: Vec<Rc<String>>, exit_code: i32, strategy: OCatchStrategy, ) -> Self { Self { stdout_lines, stderr_lines, stdcombined_lines, exit_code, strategy } } /// Getter for `stdout_lines`. This is only available if [`OCatchStrategy::StdSeparately`] was used. pub fn stdout_lines(&self) -> Option<&Vec<Rc<String>>> { self.stdout_lines.as_ref() } /// Getter for `stderr_lines`. This is only available if [`OCatchStrategy::StdSeparately`] was used. pub fn stderr_lines(&self) -> Option<&Vec<Rc<String>>> { self.stderr_lines.as_ref() } /// Getter for `stdcombined_lines`. The correctness of the ordering depends on the used [`OCatchStrategy`]. pub fn stdcombined_lines(&self) -> &Vec<Rc<String>> { &self.stdcombined_lines } /// Getter for `exit_code` of the executed child process. pub fn exit_code(&self) -> i32 { self.exit_code } /// Getter for the used [`OCatchStrategy`]. pub fn strategy(&self) -> OCatchStrategy { self.strategy } } /// Determines the strategy that is used to get STDOUT, STDERR, and "STDCOMBINED". /// Both has advantages and disadvantages. #[derive(Debug, Display, Copy, Clone)] pub enum OCatchStrategy { /// Catches all output lines of STDOUT and STDERR in correct order on a line /// by line base. There is no way to find out STDOUT-only or STDERR-only lines. StdCombined, /// Catches all output lines from STDOUT and STDERR separately. There is also a /// "STDCOMBINED" vector, but the order is not 100% correct. It's only approximately correct /// on a best effort base. If between each STDOUT/STDERR-alternating output is ≈100µs /// (a few thousand cycles) it should be definitely fine, but there is no guarantee for that. /// Also the incorrectness is not deterministic. This is because /// STDOUT and STDERR are two separate streams. Scheduling and buffering result in /// different results. StdSeparately, } #[cfg(test)] mod tests { use super::*; // RUst tests doesn't work with fork, dup2 and other fun :) // weird output.. use the test binary instead! /*#[test] fn test_fork_exec_and_catch() { let res = fork_exec_and_catch("echo", vec!["echo", "hallo"]); println!("{:#?}", res); // fork_exec_and_catch("sysctl", vec!["sysctl", "-a"]); }*/ }