├── .gitignore ├── Cargo.lock ├── tests ├── test_fn.rs ├── test_output.rs └── test_trait.rs ├── Cargo.toml ├── .github └── workflows │ └── cargo.yml ├── LICENSE ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "system" 7 | version = "0.3.4" 8 | -------------------------------------------------------------------------------- /tests/test_fn.rs: -------------------------------------------------------------------------------- 1 | use system::system; 2 | 3 | #[test] 4 | fn test() { 5 | let status = system("echo Hello, world!").expect("Failed to run command."); 6 | assert_eq!(status.code().unwrap(), 0); 7 | } 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "system" 3 | version = "0.3.4" 4 | authors = ["Brandon Fowler"] 5 | edition = "2021" 6 | description = "Cross-platform crate to easily run shell commands, similar to the C system function." 7 | repository = "https://github.com/BrandonXLF/rust-system" 8 | license = "MIT" 9 | keywords = ["shell", "system", "command", "sh", "cmd"] 10 | categories = ["os"] 11 | exclude = [".*"] 12 | -------------------------------------------------------------------------------- /tests/test_output.rs: -------------------------------------------------------------------------------- 1 | use system::system_output; 2 | 3 | #[test] 4 | fn test() { 5 | let out = system_output("echo Hello, world!").expect("Failed to run command."); 6 | let stdout = String::from_utf8_lossy(&out.stdout); 7 | 8 | #[cfg(target_os = "windows")] 9 | assert_eq!(stdout, "Hello, world!\r\n"); 10 | 11 | #[cfg(not(target_os = "windows"))] 12 | assert_eq!(stdout, "Hello, world!\n"); 13 | } 14 | -------------------------------------------------------------------------------- /tests/test_trait.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use system::System; 4 | 5 | #[test] 6 | fn test() { 7 | let out = Command::system("echo Hello, world!") 8 | .output() 9 | .expect("Failed to run command."); 10 | let stdout = String::from_utf8_lossy(&out.stdout); 11 | 12 | #[cfg(target_os = "windows")] 13 | assert_eq!(stdout, "Hello, world!\r\n"); 14 | 15 | #[cfg(not(target_os = "windows"))] 16 | assert_eq!(stdout, "Hello, world!\n"); 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/cargo.yml: -------------------------------------------------------------------------------- 1 | name: Cargo 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | os: 19 | - macos-latest 20 | - ubuntu-latest 21 | - windows-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Build 26 | run: cargo build --verbose 27 | - name: Run tests 28 | run: cargo test --verbose 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Brandon Fowler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # system 2 | 3 | Cross-platform crate to easily run shell commands, similar to the C `system` function. 4 | 5 | ## Usage 6 | 7 | ### `system` and `system_output` 8 | 9 | For simple use cases where you just need the result of a system command, the `system` and `system_output` functions can be used. 10 | 11 | `system` inherits the stdout, stderr, and stdin from the parent process whereas `system_output` captures stdout and stderr and does not inherit an stdin. 12 | 13 | An example of using `system`, 14 | 15 | ```rust 16 | use system::system; 17 | 18 | fn main() { 19 | // Prints "Hello, world!" 20 | system("echo Hello, world!").expect("Failed to run command."); 21 | } 22 | ``` 23 | 24 | An example of using `system_output`, 25 | 26 | ```rust 27 | use system::system_output; 28 | 29 | fn main() { 30 | let out = system_output("echo Hello, world!").expect("Failed to run command."); 31 | let stdout = String::from_utf8_lossy(&out.stdout); 32 | 33 | #[cfg(target_os = "windows")] 34 | assert_eq!(stdout, "Hello, world!\r\n"); 35 | 36 | #[cfg(not(target_os = "windows"))] 37 | assert_eq!(stdout, "Hello, world!\n"); 38 | } 39 | ``` 40 | 41 | ### `std::process::Command::system` 42 | 43 | For more complex uses cases where the underlying `Command` has to be modified before running the command, the `system::System` trait is implemented for `Command`. 44 | 45 | The trait adds the function `Command::system` to create `Command`s that execute shell commands. 46 | 47 | For example, 48 | 49 | ```rust 50 | use std::process::Command; 51 | 52 | use system::System; 53 | 54 | fn test() { 55 | let out = Command::system("echo Hello, world!") 56 | .output() 57 | .expect("Failed to run command."); 58 | let stdout = String::from_utf8_lossy(&out.stdout); 59 | 60 | #[cfg(target_os = "windows")] 61 | assert_eq!(stdout, "Hello, world!\r\n"); 62 | 63 | #[cfg(not(target_os = "windows"))] 64 | assert_eq!(stdout, "Hello, world!\n"); 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Cross-platform crate to easily run shell commands, similar to the C `system` function. 2 | 3 | use std::{ 4 | io, 5 | process::{Command, ExitStatus, Output}, 6 | }; 7 | 8 | #[cfg(target_os = "windows")] 9 | use std::os::windows::process::CommandExt; 10 | 11 | /// Trait to allow for the creation of objects representing a shell commands. 12 | pub trait System { 13 | /// Constructs a new `T` that runs the given shell command when executed. 14 | fn system(command: &str) -> T; 15 | } 16 | 17 | impl System for Command { 18 | #[cfg(target_os = "windows")] 19 | /// Constructs a new [Command] that runs the given shell command when executed. 20 | /// 21 | /// See [Command::new] for the default configuration. 22 | /// 23 | /// # Example 24 | /// 25 | /// ```rust 26 | /// use std::process::Command; 27 | /// 28 | /// use system::System; 29 | /// 30 | /// fn main() { 31 | /// let out = Command::system("echo Hello, world!") 32 | /// .output() 33 | /// .expect("Failed to run command."); 34 | /// let stdout = String::from_utf8_lossy(&out.stdout); 35 | /// 36 | /// #[cfg(target_os = "windows")] 37 | /// assert_eq!(stdout, "Hello, world!\r\n"); 38 | /// 39 | /// #[cfg(not(target_os = "windows"))] 40 | /// assert_eq!(stdout, "Hello, world!\n"); 41 | /// } 42 | /// ``` 43 | fn system(command: &str) -> Command { 44 | let mut rust_command = Command::new("cmd"); 45 | rust_command.arg("/c"); 46 | rust_command.raw_arg(&command); 47 | return rust_command; 48 | } 49 | 50 | #[cfg(not(target_os = "windows"))] 51 | /// Construct a new [Command] that runs the given system command when executed. 52 | /// 53 | /// See [Command::new] for the default configuration. 54 | /// 55 | /// # Example 56 | /// 57 | /// ```rust 58 | /// use std::process::Command; 59 | /// 60 | /// use system::System; 61 | /// 62 | /// fn main() { 63 | /// let out = Command::system("echo Hello, world!") 64 | /// .output() 65 | /// .expect("Failed to run command."); 66 | /// let stdout = String::from_utf8_lossy(&out.stdout); 67 | /// 68 | /// #[cfg(target_os = "windows")] 69 | /// assert_eq!(stdout, "Hello, world!\r\n"); 70 | /// 71 | /// #[cfg(not(target_os = "windows"))] 72 | /// assert_eq!(stdout, "Hello, world!\n"); 73 | /// } 74 | /// ``` 75 | fn system(command: &str) -> Command { 76 | let mut rust_command = Command::new("sh"); 77 | rust_command.arg("-c"); 78 | rust_command.arg(&command); 79 | return rust_command; 80 | } 81 | } 82 | 83 | /// Run a shell command and return the [ExitStatus]. 84 | /// 85 | /// Stdin, stdout, and stderr are inherited from the parent. 86 | /// 87 | /// # Example 88 | /// ```rust 89 | /// use system::system; 90 | /// 91 | /// fn main() { 92 | /// // Prints "Hello, world!" 93 | /// system("echo Hello, world!").expect("Failed to run command."); 94 | /// } 95 | /// ``` 96 | pub fn system(command: &str) -> io::Result { 97 | Command::system(command).status() 98 | } 99 | 100 | /// Run a shell command, capture its output, and return the [Output]. 101 | /// 102 | /// Stdout and stderr are captured and stdin is not inherited. 103 | /// 104 | /// # Example 105 | /// ```rust 106 | /// use system::system_output; 107 | /// 108 | /// fn main() { 109 | /// let out = system_output("echo Hello, world!").expect("Failed to run command."); 110 | /// let stdout = String::from_utf8_lossy(&out.stdout); 111 | /// 112 | /// #[cfg(target_os = "windows")] 113 | /// assert_eq!(stdout, "Hello, world!\r\n"); 114 | /// 115 | /// #[cfg(not(target_os = "windows"))] 116 | /// assert_eq!(stdout, "Hello, world!\n"); 117 | /// } 118 | /// ``` 119 | pub fn system_output(command: &str) -> io::Result { 120 | Command::system(command).output() 121 | } 122 | --------------------------------------------------------------------------------