├── README.md ├── .github ├── dependabot.yml └── workflows │ ├── ci-version.yml │ └── ci.yml ├── Cargo.toml ├── execute-command-macro-impl ├── README.md ├── Cargo.toml ├── LICENSE ├── src │ └── lib.rs ├── rustfmt.toml └── .gitignore ├── execute-command-tokens ├── README.md ├── Cargo.toml ├── LICENSE ├── rustfmt.toml ├── .gitignore ├── src │ └── lib.rs └── tests │ └── tests.rs ├── execute-command-macro ├── tests │ └── tests.rs ├── Cargo.toml ├── README.md ├── LICENSE ├── src │ └── lib.rs ├── rustfmt.toml └── .gitignore ├── execute ├── Cargo.toml ├── LICENSE ├── rustfmt.toml ├── tests │ ├── execute.rs │ └── execute_many.rs ├── .gitignore ├── README.md └── src │ └── lib.rs ├── LICENSE └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | See [execute/README.md](execute/README.md). -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "execute-command-tokens", 5 | "execute-command-macro-impl", 6 | "execute-command-macro", 7 | "execute" 8 | ] -------------------------------------------------------------------------------- /execute-command-macro-impl/README.md: -------------------------------------------------------------------------------- 1 | Execute Command Macro Impl 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/execute/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/execute/actions/workflows/ci.yml) 5 | 6 | See [`execute-command-macro`](https://crates.io/crates/execute-command-macro). 7 | 8 | ## Crates.io 9 | 10 | https://crates.io/crates/execute 11 | 12 | ## Documentation 13 | 14 | https://docs.rs/execute 15 | 16 | ## License 17 | 18 | [MIT](LICENSE) 19 | -------------------------------------------------------------------------------- /execute-command-tokens/README.md: -------------------------------------------------------------------------------- 1 | Execute Command Tokens 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/execute/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/execute/actions/workflows/ci.yml) 5 | 6 | Parse command strings. 7 | 8 | See [`execute`](https://crates.io/crates/execute). 9 | 10 | ## Crates.io 11 | 12 | https://crates.io/crates/execute 13 | 14 | ## Documentation 15 | 16 | https://docs.rs/execute 17 | 18 | ## License 19 | 20 | [MIT](LICENSE) 21 | -------------------------------------------------------------------------------- /execute-command-tokens/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "execute-command-tokens" 3 | version = "0.1.9" 4 | authors = ["Magic Len "] 5 | edition = "2021" 6 | rust-version = "1.80" 7 | repository = "https://github.com/magiclen/execute" 8 | homepage = "https://magiclen.org/execute" 9 | keywords = ["execute", "command"] 10 | categories = ["parser-implementations"] 11 | description= "Parse command strings." 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /execute-command-macro/tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(target_os = "linux")] 2 | 3 | #[macro_use] 4 | extern crate execute_command_macro; 5 | 6 | #[test] 7 | fn command() { 8 | let mut command = command!("sh -c \"echo '123 456' | cut -d ' ' -f 1\""); 9 | 10 | let output = command.output().unwrap(); 11 | 12 | assert_eq!(b"123\n", output.stdout.as_slice()); 13 | } 14 | 15 | #[test] 16 | fn command_args() { 17 | let mut command = command_args!("sh", "-c", "echo '123 456' | cut -d ' ' -f 1"); 18 | 19 | let output = command.output().unwrap(); 20 | 21 | assert_eq!(b"123\n", output.stdout.as_slice()); 22 | } 23 | -------------------------------------------------------------------------------- /execute-command-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "execute-command-macro" 3 | version = "0.1.11" 4 | rust-version = "1.80" 5 | authors = ["Magic Len "] 6 | edition = "2021" 7 | repository = "https://github.com/magiclen/execute" 8 | homepage = "https://magiclen.org/execute" 9 | keywords = ["execute", "command"] 10 | categories = ["parser-implementations"] 11 | description = "Create `Command` instances using the `command!` macro or the `command_args!` marco." 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 14 | 15 | [dependencies] 16 | execute-command-macro-impl = { version = "0.1", path = "../execute-command-macro-impl" } -------------------------------------------------------------------------------- /execute-command-macro-impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "execute-command-macro-impl" 3 | version = "0.1.12" 4 | authors = ["Magic Len "] 5 | edition = "2021" 6 | rust-version = "1.80" 7 | repository = "https://github.com/magiclen/execute" 8 | homepage = "https://magiclen.org/execute" 9 | keywords = ["execute", "command"] 10 | categories = ["parser-implementations"] 11 | description = "Create `Command` instances using the `command!` macro." 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | execute-command-tokens = { version = "0.1", path = "../execute-command-tokens" } 20 | 21 | syn = "2" 22 | quote = "1" -------------------------------------------------------------------------------- /execute/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "execute" 3 | version = "0.2.15" 4 | authors = ["Magic Len "] 5 | edition = "2021" 6 | rust-version = "1.80" 7 | repository = "https://github.com/magiclen/execute" 8 | homepage = "https://magiclen.org/execute" 9 | keywords = ["execute", "redirect", "process", "command", "pipe"] 10 | categories = ["parser-implementations"] 11 | description = "A library for extending `Command` in order to execute programs more easily." 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 14 | 15 | [dependencies] 16 | execute-command-tokens = { version = "0.1", path = "../execute-command-tokens" } 17 | execute-command-macro = { version = "0.1.1", path = "../execute-command-macro" } 18 | 19 | generic-array = "1" -------------------------------------------------------------------------------- /execute-command-macro/README.md: -------------------------------------------------------------------------------- 1 | Execute Command Macro 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/execute/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/execute/actions/workflows/ci.yml) 5 | 6 | Create `Command` instances using the `command!` macro or the `command_args!` macro. 7 | 8 | Also see [`execute`](https://crates.io/crates/execute). 9 | 10 | ## Examples 11 | 12 | ```rust 13 | #[macro_use] extern crate execute_command_macro; 14 | 15 | let command = command!("program arg1 arg2 'arg 3' -opt1 -opt2"); 16 | ``` 17 | 18 | ```rust 19 | #[macro_use] extern crate execute_command_macro; 20 | 21 | let command = command_args!("program", "arg1", "arg2", "-opt1", "-opt2"); 22 | ``` 23 | 24 | ## Crates.io 25 | 26 | https://crates.io/crates/execute 27 | 28 | ## Documentation 29 | 30 | https://docs.rs/execute 31 | 32 | ## License 33 | 34 | [MIT](LICENSE) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /execute/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /execute-command-macro/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /execute-command-macro-impl/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /execute-command-tokens/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /execute-command-macro-impl/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Execute Command Macro Impl 3 | 4 | See [`execute-command-macro`](https://crates.io/crates/execute-command-macro). 5 | */ 6 | 7 | use execute_command_tokens::command_tokens; 8 | use proc_macro::TokenStream; 9 | use quote::quote; 10 | use syn::{parse_macro_input, LitStr}; 11 | 12 | #[proc_macro] 13 | pub fn command(input: TokenStream) -> TokenStream { 14 | let s = parse_macro_input!(input as LitStr).value(); 15 | 16 | let tokens = command_tokens(s); 17 | 18 | let tokens_length = tokens.len(); 19 | 20 | let command = match tokens_length { 21 | 0 => { 22 | quote! { 23 | ::std::process::Command::new("") 24 | } 25 | }, 26 | 1 => { 27 | let program = &tokens[0]; 28 | 29 | quote! { 30 | ::std::process::Command::new(#program) 31 | } 32 | }, 33 | _ => { 34 | let program = &tokens[0]; 35 | let args = &tokens[1..]; 36 | 37 | quote! { 38 | { 39 | let mut command = ::std::process::Command::new(#program); 40 | 41 | command.args(&[#(#args,)*]); 42 | 43 | command 44 | } 45 | } 46 | }, 47 | }; 48 | 49 | command.into() 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/ci-version.yml: -------------------------------------------------------------------------------- 1 | name: CI-version 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | tests: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - ubuntu-latest 18 | - macos-latest 19 | - windows-latest 20 | toolchain: 21 | - stable 22 | - nightly 23 | features: 24 | - 25 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 26 | runs-on: ${{ matrix.os }} 27 | steps: 28 | - uses: actions/checkout@v5 29 | - uses: actions-rust-lang/setup-rust-toolchain@v1 30 | with: 31 | toolchain: ${{ matrix.toolchain }} 32 | - run: cargo test --release ${{ matrix.features }} 33 | - run: cargo doc --release ${{ matrix.features }} 34 | 35 | MSRV: 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | os: 40 | - ubuntu-latest 41 | - macos-latest 42 | - windows-latest 43 | toolchain: 44 | - "1.80" 45 | features: 46 | - 47 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 48 | runs-on: ${{ matrix.os }} 49 | steps: 50 | - uses: actions/checkout@v5 51 | - uses: actions-rust-lang/setup-rust-toolchain@v1 52 | with: 53 | toolchain: ${{ matrix.toolchain }} 54 | - run: cargo test --release --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /execute-command-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Execute Command Macro 3 | 4 | Create `Command` instances using the `command!` macro or the `command_args!` macro. 5 | 6 | Also see [`execute`](https://crates.io/crates/execute). 7 | 8 | ## Examples 9 | 10 | ```rust 11 | #[macro_use] extern crate execute_command_macro; 12 | 13 | let command = command!("program arg1 arg2 'arg 3' -opt1 -opt2"); 14 | ``` 15 | 16 | ```rust 17 | #[macro_use] extern crate execute_command_macro; 18 | 19 | let command = command_args!("program", "arg1", "arg2", "-opt1", "-opt2"); 20 | ``` 21 | */ 22 | 23 | /** 24 | Generate the statements at compile time to create a `Command` instance by a command string. 25 | 26 | ```rust 27 | #[macro_use] extern crate execute_command_macro; 28 | 29 | let command = command!("program arg1 arg2 -opt1 -opt2"); 30 | ``` 31 | */ 32 | pub use execute_command_macro_impl::command; 33 | 34 | /** 35 | Create a `Command` instance by inputting args separately. 36 | 37 | ```rust 38 | #[macro_use] extern crate execute_command_macro; 39 | 40 | let command = command_args!("program", "arg1", "arg2", "-opt1", "-opt2"); 41 | ``` 42 | */ 43 | #[macro_export] 44 | macro_rules! command_args { 45 | ($program:expr $(,)*) => { 46 | ::std::process::Command::new($program) 47 | }; 48 | ($program:expr, $arg:expr $(, $args:expr)* $(,)*) => { 49 | { 50 | let mut command = ::std::process::Command::new($program); 51 | 52 | command.arg(&$arg)$(.arg(&$args))*; 53 | 54 | command 55 | } 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | rustfmt: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v5 13 | - uses: actions-rust-lang/setup-rust-toolchain@v1 14 | with: 15 | toolchain: nightly 16 | components: rustfmt 17 | - uses: actions-rust-lang/rustfmt@v1 18 | 19 | clippy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v5 23 | - uses: actions-rust-lang/setup-rust-toolchain@v1 24 | with: 25 | components: clippy 26 | - run: cargo clippy --all-targets --all-features -- -D warnings 27 | 28 | tests: 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: 33 | - ubuntu-latest 34 | - macos-latest 35 | - windows-latest 36 | toolchain: 37 | - stable 38 | - nightly 39 | features: 40 | - 41 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 42 | runs-on: ${{ matrix.os }} 43 | steps: 44 | - uses: actions/checkout@v5 45 | - uses: actions-rust-lang/setup-rust-toolchain@v1 46 | with: 47 | toolchain: ${{ matrix.toolchain }} 48 | - run: cargo test ${{ matrix.features }} 49 | - run: cargo doc ${{ matrix.features }} 50 | 51 | MSRV: 52 | strategy: 53 | fail-fast: false 54 | matrix: 55 | os: 56 | - ubuntu-latest 57 | - macos-latest 58 | - windows-latest 59 | toolchain: 60 | - "1.80" 61 | features: 62 | - 63 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 64 | runs-on: ${{ matrix.os }} 65 | steps: 66 | - uses: actions/checkout@v5 67 | - uses: actions-rust-lang/setup-rust-toolchain@v1 68 | with: 69 | toolchain: ${{ matrix.toolchain }} 70 | - run: cargo test --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /execute/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /execute-command-macro/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /execute-command-tokens/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /execute-command-macro-impl/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /execute/tests/execute.rs: -------------------------------------------------------------------------------- 1 | #![cfg(target_os = "linux")] 2 | 3 | use std::{ 4 | io::Cursor, 5 | process::{Command, Stdio}, 6 | }; 7 | 8 | use execute::Execute; 9 | 10 | #[test] 11 | fn execute() { 12 | let mut command = Command::new("true"); 13 | 14 | assert_eq!(Some(0), command.execute().unwrap()); 15 | 16 | let mut command = Command::new("false"); 17 | 18 | assert_ne!(0, command.execute().unwrap().unwrap()); 19 | } 20 | 21 | #[test] 22 | fn execute_output() { 23 | let mut command = Command::new("cat"); 24 | 25 | command.arg("/proc/cpuinfo"); 26 | 27 | command.stdout(Stdio::piped()); 28 | 29 | let output = command.execute_output().unwrap(); 30 | 31 | assert!(output.stdout.starts_with(b"processor")); 32 | } 33 | 34 | #[test] 35 | fn execute_check_exit_status_code() { 36 | let mut command = Command::new("true"); 37 | 38 | assert!(command.execute_check_exit_status_code(0).is_ok()); 39 | 40 | let mut command = Command::new("false"); 41 | 42 | assert!(command.execute_check_exit_status_code(0).is_err()); 43 | } 44 | 45 | #[test] 46 | fn execute_input() { 47 | let mut command = Command::new("bc"); 48 | 49 | assert_eq!(Some(0), command.execute_input("1 + 1\n").unwrap()); 50 | } 51 | 52 | #[test] 53 | fn execute_input_output() { 54 | let mut command = Command::new("bc"); 55 | 56 | command.stdout(Stdio::piped()); 57 | 58 | let output = command.execute_input_output("1 + 1\n").unwrap(); 59 | 60 | assert_eq!(b"2\n", output.stdout.as_slice()); 61 | } 62 | 63 | #[test] 64 | fn execute_input_reader() { 65 | let mut command = Command::new("bc"); 66 | 67 | let mut reader = Cursor::new("1 + 1\n"); 68 | 69 | assert_eq!(Some(0), command.execute_input_reader(&mut reader).unwrap()); 70 | } 71 | 72 | #[test] 73 | fn execute_input_reader_output() { 74 | let mut command = Command::new("bc"); 75 | 76 | command.stdout(Stdio::piped()); 77 | 78 | let mut reader = Cursor::new("1 + 1\n"); 79 | 80 | let output = command.execute_input_reader_output(&mut reader).unwrap(); 81 | 82 | assert_eq!(b"2\n", output.stdout.as_slice()); 83 | } 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij+all 2 | 3 | ### Intellij+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | ### Intellij+all Patch ### 66 | # Ignores the whole .idea folder and all .iml files 67 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 68 | 69 | .idea/ 70 | 71 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 72 | 73 | *.iml 74 | modules.xml 75 | .idea/misc.xml 76 | *.ipr 77 | 78 | 79 | # End of https://www.gitignore.io/api/intellij+all 80 | 81 | 82 | ### Rust ### 83 | # Generated by Cargo 84 | # will have compiled files and executables 85 | /target/ 86 | 87 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 88 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 89 | Cargo.lock 90 | 91 | # These are backup files generated by rustfmt 92 | **/*.rs.bk 93 | 94 | 95 | # End of https://www.gitignore.io/api/rust 96 | -------------------------------------------------------------------------------- /execute/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij+all 2 | 3 | ### Intellij+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | ### Intellij+all Patch ### 66 | # Ignores the whole .idea folder and all .iml files 67 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 68 | 69 | .idea/ 70 | 71 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 72 | 73 | *.iml 74 | modules.xml 75 | .idea/misc.xml 76 | *.ipr 77 | 78 | 79 | # End of https://www.gitignore.io/api/intellij+all 80 | 81 | 82 | ### Rust ### 83 | # Generated by Cargo 84 | # will have compiled files and executables 85 | /target/ 86 | 87 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 88 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 89 | Cargo.lock 90 | 91 | # These are backup files generated by rustfmt 92 | **/*.rs.bk 93 | 94 | 95 | # End of https://www.gitignore.io/api/rust 96 | -------------------------------------------------------------------------------- /execute-command-macro/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij+all 2 | 3 | ### Intellij+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | ### Intellij+all Patch ### 66 | # Ignores the whole .idea folder and all .iml files 67 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 68 | 69 | .idea/ 70 | 71 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 72 | 73 | *.iml 74 | modules.xml 75 | .idea/misc.xml 76 | *.ipr 77 | 78 | 79 | # End of https://www.gitignore.io/api/intellij+all 80 | 81 | 82 | ### Rust ### 83 | # Generated by Cargo 84 | # will have compiled files and executables 85 | /target/ 86 | 87 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 88 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 89 | Cargo.lock 90 | 91 | # These are backup files generated by rustfmt 92 | **/*.rs.bk 93 | 94 | 95 | # End of https://www.gitignore.io/api/rust 96 | -------------------------------------------------------------------------------- /execute-command-tokens/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij+all 2 | 3 | ### Intellij+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | ### Intellij+all Patch ### 66 | # Ignores the whole .idea folder and all .iml files 67 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 68 | 69 | .idea/ 70 | 71 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 72 | 73 | *.iml 74 | modules.xml 75 | .idea/misc.xml 76 | *.ipr 77 | 78 | 79 | # End of https://www.gitignore.io/api/intellij+all 80 | 81 | 82 | ### Rust ### 83 | # Generated by Cargo 84 | # will have compiled files and executables 85 | /target/ 86 | 87 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 88 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 89 | Cargo.lock 90 | 91 | # These are backup files generated by rustfmt 92 | **/*.rs.bk 93 | 94 | 95 | # End of https://www.gitignore.io/api/rust 96 | -------------------------------------------------------------------------------- /execute-command-macro-impl/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/intellij+all 2 | 3 | ### Intellij+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | ### Intellij+all Patch ### 66 | # Ignores the whole .idea folder and all .iml files 67 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 68 | 69 | .idea/ 70 | 71 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 72 | 73 | *.iml 74 | modules.xml 75 | .idea/misc.xml 76 | *.ipr 77 | 78 | 79 | # End of https://www.gitignore.io/api/intellij+all 80 | 81 | 82 | ### Rust ### 83 | # Generated by Cargo 84 | # will have compiled files and executables 85 | /target/ 86 | 87 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 88 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 89 | Cargo.lock 90 | 91 | # These are backup files generated by rustfmt 92 | **/*.rs.bk 93 | 94 | 95 | # End of https://www.gitignore.io/api/rust 96 | -------------------------------------------------------------------------------- /execute-command-tokens/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Execute Command Tokens 3 | 4 | Parse command strings. 5 | 6 | See [`execute`](https://crates.io/crates/execute). 7 | */ 8 | 9 | /// Parse a command string. 10 | pub fn command_tokens>(cmd: S) -> Vec { 11 | let cmd = cmd.as_ref(); 12 | 13 | let mut tokens = Vec::with_capacity(1); 14 | let mut string_buffer = String::new(); 15 | 16 | let mut append_mode = false; 17 | let mut quote_mode = false; 18 | let mut quote_mode_ending = false; // to deal with '123''456' -> 123456 19 | let mut quote_char = ' '; 20 | let mut escaping = false; 21 | 22 | for c in cmd.chars() { 23 | if escaping { 24 | append_mode = true; 25 | escaping = false; 26 | 27 | string_buffer.push(c); 28 | } else if c.is_whitespace() { 29 | if append_mode { 30 | if quote_mode { 31 | string_buffer.push(c); 32 | } else { 33 | append_mode = false; 34 | 35 | tokens.push(string_buffer); 36 | string_buffer = String::new(); 37 | } 38 | } else if quote_mode_ending { 39 | quote_mode_ending = false; 40 | 41 | tokens.push(string_buffer); 42 | string_buffer = String::new(); 43 | } 44 | } else { 45 | match c { 46 | '"' | '\'' => { 47 | if append_mode { 48 | if quote_mode { 49 | if quote_char == c { 50 | append_mode = false; 51 | quote_mode = false; 52 | quote_mode_ending = true; 53 | } else { 54 | string_buffer.push(c); 55 | } 56 | } else { 57 | quote_mode = true; 58 | quote_char = c; 59 | } 60 | } else { 61 | append_mode = true; 62 | quote_mode = true; 63 | quote_char = c; 64 | } 65 | }, 66 | '\\' => { 67 | escaping = true; 68 | }, 69 | _ => { 70 | append_mode = true; 71 | escaping = false; 72 | 73 | string_buffer.push(c); 74 | }, 75 | } 76 | } 77 | } 78 | 79 | if append_mode || quote_mode_ending { 80 | tokens.push(string_buffer); 81 | } 82 | 83 | tokens 84 | } 85 | -------------------------------------------------------------------------------- /execute/tests/execute_many.rs: -------------------------------------------------------------------------------- 1 | #![cfg(target_os = "linux")] 2 | 3 | use std::{ 4 | io::Cursor, 5 | process::{Command, Stdio}, 6 | }; 7 | 8 | use execute::Execute; 9 | 10 | #[test] 11 | fn execute_multiple() { 12 | let mut command1 = Command::new("echo"); 13 | 14 | command1.arg("abc"); 15 | 16 | let mut command2 = Command::new("grep"); 17 | 18 | command2.arg("b"); 19 | 20 | assert_eq!(Some(0), command1.execute_multiple(&mut [&mut command2]).unwrap()); 21 | 22 | let mut command3 = Command::new("grep"); 23 | 24 | command3.arg("d"); 25 | 26 | assert_ne!(0, command1.execute_multiple(&mut [&mut command3]).unwrap().unwrap()); 27 | } 28 | 29 | #[test] 30 | fn execute_multiple_output() { 31 | let mut command1 = Command::new("cat"); 32 | 33 | command1.arg("/proc/cpuinfo"); 34 | 35 | let mut command2 = Command::new("grep"); 36 | 37 | command2.arg("cpu MHz"); 38 | 39 | command2.stdout(Stdio::piped()); 40 | 41 | let output = command1.execute_multiple_output(&mut [&mut command2]).unwrap(); 42 | 43 | assert!(output.stdout.starts_with(b"cpu MHz")); 44 | } 45 | 46 | #[test] 47 | fn execute_multiple_input() { 48 | let mut command1 = Command::new("bc"); 49 | 50 | let mut command2 = Command::new("grep"); 51 | 52 | command2.arg("2"); 53 | 54 | assert_eq!( 55 | Some(0), 56 | command1.execute_multiple_input("1 + 1\nquit\n", &mut [&mut command2]).unwrap() 57 | ); 58 | } 59 | 60 | #[test] 61 | fn execute_multiple_input_output() { 62 | let mut command1 = Command::new("bc"); 63 | 64 | let mut command2 = Command::new("grep"); 65 | 66 | command2.arg("2"); 67 | 68 | command2.stdout(Stdio::piped()); 69 | 70 | let output = command1 71 | .execute_multiple_input_output("3 + 1\n\n3 - 1\nquit\n", &mut [&mut command2]) 72 | .unwrap(); 73 | 74 | assert_eq!(b"2\n", output.stdout.as_slice()); 75 | } 76 | 77 | #[test] 78 | fn execute_multiple_input_reader() { 79 | let mut command1 = Command::new("bc"); 80 | 81 | let mut command2 = Command::new("grep"); 82 | 83 | command2.arg("2"); 84 | 85 | let mut reader = Cursor::new("1 + 1\nquit\n"); 86 | 87 | assert_eq!( 88 | Some(0), 89 | command1.execute_multiple_input_reader(&mut reader, &mut [&mut command2]).unwrap() 90 | ); 91 | } 92 | 93 | #[test] 94 | fn execute_multiple_input_reader_output() { 95 | let mut command1 = Command::new("bc"); 96 | 97 | let mut command2 = Command::new("grep"); 98 | 99 | command2.arg("2"); 100 | 101 | command2.stdout(Stdio::piped()); 102 | 103 | let mut reader = Cursor::new("3 + 1\n\n3 - 1\nquit\n"); 104 | 105 | let output = 106 | command1.execute_multiple_input_reader_output(&mut reader, &mut [&mut command2]).unwrap(); 107 | 108 | assert_eq!(b"2\n", output.stdout.as_slice()); 109 | } 110 | -------------------------------------------------------------------------------- /execute-command-tokens/tests/tests.rs: -------------------------------------------------------------------------------- 1 | use execute_command_tokens::command_tokens; 2 | 3 | #[test] 4 | fn v1() { 5 | assert_eq!(vec!["program"], command_tokens("program")); 6 | assert_eq!(vec!["program"], command_tokens("'program'")); 7 | assert_eq!(vec!["program"], command_tokens("\"program\"")); 8 | } 9 | 10 | #[test] 11 | fn v2() { 12 | assert_eq!(vec!["program", "arg1"], command_tokens("program arg1")); 13 | assert_eq!(vec!["program", "arg1"], command_tokens("program 'arg1'")); 14 | assert_eq!(vec!["program", "arg1"], command_tokens("program \"arg1\"")); 15 | 16 | assert_eq!(vec!["program", "arg1"], command_tokens("'program' arg1")); 17 | assert_eq!(vec!["program", "arg1"], command_tokens("'program' 'arg1'")); 18 | assert_eq!(vec!["program", "arg1"], command_tokens("'program' \"arg1\"")); 19 | 20 | assert_eq!(vec!["program", "arg1"], command_tokens("\"program\" arg1")); 21 | assert_eq!(vec!["program", "arg1"], command_tokens("\"program\" 'arg1'")); 22 | assert_eq!(vec!["program", "arg1"], command_tokens("\"program\" \"arg1\"")); 23 | } 24 | 25 | #[test] 26 | fn v3() { 27 | assert_eq!(vec!["program", "arg1", "arg2"], command_tokens("program arg1 arg2")); 28 | assert_eq!(vec!["program", "arg1 arg2"], command_tokens("program 'arg1 arg2'")); 29 | assert_eq!(vec!["program", "arg1 arg2"], command_tokens("program \"arg1 arg2\"")); 30 | } 31 | 32 | #[test] 33 | fn v4() { 34 | assert_eq!(vec!["program", "arg1 arg2"], command_tokens(r"program arg1\ arg2")); 35 | } 36 | 37 | #[test] 38 | fn v5() { 39 | assert_eq!(vec!["program", "arg1", "arg2", " "], command_tokens("program arg1 arg2 ' '")); 40 | assert_eq!(vec!["program", "arg1", "arg2", " "], command_tokens("program arg1 arg2 ' '")); 41 | assert_eq!(vec!["program", "arg1", " ", "arg2"], command_tokens("program arg1 ' ' arg2")); 42 | assert_eq!(vec!["program", "arg1", " ", "arg2"], command_tokens("program arg1 ' ' arg2")); 43 | assert_eq!(vec!["program", "arg1", "arg2", " "], command_tokens("program arg1 arg2 \" \"")); 44 | assert_eq!(vec!["program", "arg1", "arg2", " "], command_tokens("program arg1 arg2 \" \"")); 45 | assert_eq!(vec!["program", "arg1", " ", "arg2"], command_tokens("program arg1 \" \" arg2")); 46 | assert_eq!(vec!["program", "arg1", " ", "arg2"], command_tokens("program arg1 \" \" arg2")); 47 | assert_eq!(vec!["program", "arg1", "arg2", " "], command_tokens(r"program arg1 arg2 \ ")); 48 | assert_eq!(vec!["program", "arg1", "arg2", " "], command_tokens(r"program arg1 arg2 \ \ ")); 49 | assert_eq!(vec!["program", "arg1", " ", "arg2"], command_tokens(r"program arg1 \ arg2")); 50 | assert_eq!(vec!["program", "arg1", " ", "arg2"], command_tokens(r"program arg1 \ \ arg2")); 51 | } 52 | 53 | #[test] 54 | fn v6() { 55 | assert_eq!(vec!["program", "123 \"456\""], command_tokens("program '123 \"456\"'")); 56 | assert_eq!(vec!["program", "123 '456'"], command_tokens("program \"123 '456'\"")); 57 | } 58 | 59 | #[test] 60 | fn v7() { 61 | assert_eq!(vec!["program", "123456"], command_tokens("program 123'456'")); 62 | assert_eq!(vec!["program", "123456"], command_tokens("program 123\"456\"")); 63 | 64 | assert_eq!(vec!["program", "123456"], command_tokens("program '123'456")); 65 | assert_eq!(vec!["program", "123456"], command_tokens("program \"123\"456")); 66 | 67 | assert_eq!(vec!["program", "123456"], command_tokens("program '123''456'")); 68 | assert_eq!(vec!["program", "123456"], command_tokens("program \"123\"\"456\"")); 69 | 70 | assert_eq!(vec!["program", "123456"], command_tokens("program '123'\"456\"")); 71 | assert_eq!(vec!["program", "123456"], command_tokens("program \"123\"'456'")); 72 | } 73 | 74 | #[test] 75 | fn tolerance_v1() { 76 | assert_eq!(vec!["program", "1234 "], command_tokens("program '1234 ")); 77 | assert_eq!(vec!["program", "1234 "], command_tokens("program \"1234 ")); 78 | 79 | assert_eq!(vec!["program", "1234\" "], command_tokens("program '1234\" ")); 80 | assert_eq!(vec!["program", "1234' "], command_tokens("program \"1234' ")); 81 | } 82 | -------------------------------------------------------------------------------- /execute/README.md: -------------------------------------------------------------------------------- 1 | Execute 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/execute/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/execute/actions/workflows/ci.yml) 5 | 6 | This library is used for extending `Command` in order to execute programs more easily. 7 | 8 | ## Usage 9 | 10 | ```rust 11 | use std::process::Command; 12 | 13 | use execute::Execute; 14 | 15 | // ... 16 | ``` 17 | 18 | ### Verify the Program 19 | 20 | Since `Command` is used for spawning a process of a command and the executed progrom is external which may not exist or may not be the program that we expected, we usually need to verify the external program at runtime. 21 | 22 | The `execute_check_exit_status_code` method can be used to execute a command and check its exit status. For example, 23 | 24 | ```rust 25 | use std::process::Command; 26 | 27 | use execute::Execute; 28 | 29 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 30 | 31 | let mut first_command = Command::new(FFMPEG_PATH); 32 | 33 | first_command.arg("-version"); 34 | 35 | if first_command.execute_check_exit_status_code(0).is_err() { 36 | eprintln!("The path `{}` is not a correct FFmpeg executable binary file.", FFMPEG_PATH); 37 | } 38 | ``` 39 | 40 | ### Execute and Get the Exit Status 41 | 42 | ```rust 43 | use std::process::Command; 44 | 45 | use execute::Execute; 46 | 47 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 48 | 49 | let mut command = Command::new(FFMPEG_PATH); 50 | 51 | command.arg("-i"); 52 | command.arg("/path/to/media-file"); 53 | command.arg("/path/to/output-file"); 54 | 55 | if let Some(exit_code) = command.execute().unwrap() { 56 | if exit_code == 0 { 57 | println!("Ok."); 58 | } else { 59 | eprintln!("Failed."); 60 | } 61 | } else { 62 | eprintln!("Interrupted!"); 63 | } 64 | ``` 65 | 66 | ### Execute and Get the Output 67 | 68 | #### Output to the Screen 69 | 70 | ```rust 71 | use std::process::Command; 72 | 73 | use execute::Execute; 74 | 75 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 76 | 77 | let mut command = Command::new(FFMPEG_PATH); 78 | 79 | command.arg("-i"); 80 | command.arg("/path/to/media-file"); 81 | command.arg("/path/to/output-file"); 82 | 83 | let output = command.execute_output().unwrap(); 84 | 85 | if let Some(exit_code) = output.status.code() { 86 | if exit_code == 0 { 87 | println!("Ok."); 88 | } else { 89 | eprintln!("Failed."); 90 | } 91 | } else { 92 | eprintln!("Interrupted!"); 93 | } 94 | ``` 95 | 96 | #### Output to Memory (Captured) 97 | 98 | ```rust 99 | use std::process::{Command, Stdio}; 100 | 101 | use execute::Execute; 102 | 103 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 104 | 105 | let mut command = Command::new(FFMPEG_PATH); 106 | 107 | command.arg("-i"); 108 | command.arg("/path/to/media-file"); 109 | command.arg("/path/to/output-file"); 110 | 111 | command.stdout(Stdio::piped()); 112 | command.stderr(Stdio::piped()); 113 | 114 | let output = command.execute_output().unwrap(); 115 | 116 | if let Some(exit_code) = output.status.code() { 117 | if exit_code == 0 { 118 | println!("Ok."); 119 | } else { 120 | eprintln!("Failed."); 121 | } 122 | } else { 123 | eprintln!("Interrupted!"); 124 | } 125 | 126 | println!("{}", String::from_utf8(output.stdout).unwrap()); 127 | println!("{}", String::from_utf8(output.stderr).unwrap()); 128 | ``` 129 | 130 | ### Execute and Input Data 131 | 132 | #### Input In-memory Data 133 | 134 | ```rust 135 | use std::process::{Command, Stdio}; 136 | 137 | use execute::Execute; 138 | 139 | let mut bc_command = Command::new("bc"); 140 | 141 | bc_command.stdout(Stdio::piped()); 142 | 143 | let output = bc_command.execute_input_output("2^99\n").unwrap(); 144 | 145 | println!("Answer: {}", String::from_utf8(output.stdout).unwrap().trim_end()); 146 | ``` 147 | 148 | #### Input from a Reader 149 | 150 | ```rust 151 | use std::process::{Command, Stdio}; 152 | use std::fs::File; 153 | 154 | use execute::Execute; 155 | 156 | let mut cat_command = Command::new("cat"); 157 | 158 | cat_command.stdout(Stdio::piped()); 159 | 160 | let mut file = File::open("Cargo.toml").unwrap(); 161 | 162 | let output = cat_command.execute_input_reader_output(&mut file).unwrap(); 163 | 164 | println!("{}", String::from_utf8(output.stdout).unwrap()); 165 | ``` 166 | 167 | By default, the buffer size is 256 bytes. If you want to change that, you can use the `_reader_output2` or `_reader2` methods and define a length explicitly. 168 | 169 | For example, to change the buffer size to 4096 bytes, 170 | 171 | ```rust 172 | use std::process::{Command, Stdio}; 173 | use std::fs::File; 174 | 175 | use execute::generic_array::typenum::U4096; 176 | use execute::Execute; 177 | 178 | let mut cat_command = Command::new("cat"); 179 | 180 | cat_command.stdout(Stdio::piped()); 181 | 182 | let mut file = File::open("Cargo.toml").unwrap(); 183 | 184 | let output = cat_command.execute_input_reader_output2::(&mut file).unwrap(); 185 | 186 | println!("{}", String::from_utf8(output.stdout).unwrap()); 187 | ``` 188 | 189 | ### Execute Multiple Commands and Pipe Them Together 190 | 191 | ```rust 192 | use std::process::{Command, Stdio}; 193 | 194 | use execute::Execute; 195 | 196 | let mut command1 = Command::new("echo"); 197 | command1.arg("HELLO WORLD"); 198 | 199 | let mut command2 = Command::new("cut"); 200 | command2.arg("-d").arg(" ").arg("-f").arg("1"); 201 | 202 | let mut command3 = Command::new("tr"); 203 | command3.arg("A-Z").arg("a-z"); 204 | 205 | command3.stdout(Stdio::piped()); 206 | 207 | let output = command1.execute_multiple_output(&mut [&mut command2, &mut command3]).unwrap(); 208 | 209 | assert_eq!(b"hello\n", output.stdout.as_slice()); 210 | ``` 211 | 212 | ### Run a Command String in the Current Shell 213 | 214 | The `shell` function can be used to create a `Command` instance with a single command string instead of a program name and scattered arguments. 215 | 216 | ```rust 217 | use std::process::{Command, Stdio}; 218 | 219 | use execute::{Execute, shell}; 220 | 221 | let mut command = shell("cat /proc/meminfo"); 222 | 223 | command.stdout(Stdio::piped()); 224 | 225 | let output = command.execute_output().unwrap(); 226 | 227 | println!("{}", String::from_utf8(output.stdout).unwrap()); 228 | ``` 229 | 230 | ### Parse a Command String at Runtime 231 | 232 | The `command` function can be used to create a `Command` instance with a single command string instead of a program name and scattered arguments. The difference between the `shell` function and the `command` function is that the former is interpreted by the current shell while the latter is parsed by this crate. 233 | 234 | ```rust 235 | use std::process::{Command, Stdio}; 236 | 237 | use execute::{Execute, command}; 238 | 239 | let mut command = command("cat '/proc/meminfo'"); 240 | 241 | command.stdout(Stdio::piped()); 242 | 243 | let output = command.execute_output().unwrap(); 244 | 245 | println!("{}", String::from_utf8(output.stdout).unwrap()); 246 | ``` 247 | 248 | ### Parse a Command String at Compile Time 249 | 250 | The `command!` macro can be used to create a `Command` instance with a single command string literal instead of a program name and scattered arguments. 251 | 252 | ```rust 253 | use std::process::{Command, Stdio}; 254 | 255 | use execute::Execute; 256 | 257 | let mut command = execute::command!("cat '/proc/meminfo'"); 258 | 259 | command.stdout(Stdio::piped()); 260 | 261 | let output = command.execute_output().unwrap(); 262 | 263 | println!("{}", String::from_utf8(output.stdout).unwrap()); 264 | ``` 265 | 266 | ### Create a `Command` Instance by Providing Arguments Separately 267 | 268 | The `command_args!` macro can be used to create a `Command` instance with a program name and arguments separately. The program name and arguments can be non-literal. 269 | 270 | ```rust 271 | use std::process::{Command, Stdio}; 272 | 273 | use execute::Execute; 274 | 275 | let mut command = execute::command_args!("cat", "/proc/meminfo"); 276 | 277 | command.stdout(Stdio::piped()); 278 | 279 | let output = command.execute_output().unwrap(); 280 | 281 | println!("{}", String::from_utf8(output.stdout).unwrap()); 282 | ``` 283 | 284 | ## Crates.io 285 | 286 | https://crates.io/crates/execute 287 | 288 | ## Documentation 289 | 290 | https://docs.rs/execute 291 | 292 | ## License 293 | 294 | [MIT](LICENSE) 295 | -------------------------------------------------------------------------------- /execute/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Execute 3 | 4 | This library is used for extending `Command` in order to execute programs more easily. 5 | 6 | ## Usage 7 | 8 | ```rust 9 | use std::process::Command; 10 | 11 | use execute::Execute; 12 | 13 | // ... 14 | ``` 15 | 16 | ### Verify the Program 17 | 18 | Since `Command` is used for spawning a process of a command and the executed progrom is external which may not exist or may not be the program that we expected, we usually need to verify the external program at runtime. 19 | 20 | The `execute_check_exit_status_code` method can be used to execute a command and check its exit status. For example, 21 | 22 | ```rust 23 | use std::process::Command; 24 | 25 | use execute::Execute; 26 | 27 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 28 | 29 | let mut first_command = Command::new(FFMPEG_PATH); 30 | 31 | first_command.arg("-version"); 32 | 33 | if first_command.execute_check_exit_status_code(0).is_err() { 34 | eprintln!("The path `{}` is not a correct FFmpeg executable binary file.", FFMPEG_PATH); 35 | } 36 | ``` 37 | 38 | ### Execute and Get the Exit Status 39 | 40 | ```rust,ignore 41 | use std::process::Command; 42 | 43 | use execute::Execute; 44 | 45 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 46 | 47 | let mut command = Command::new(FFMPEG_PATH); 48 | 49 | command.arg("-i"); 50 | command.arg("/path/to/media-file"); 51 | command.arg("/path/to/output-file"); 52 | 53 | if let Some(exit_code) = command.execute().unwrap() { 54 | if exit_code == 0 { 55 | println!("Ok."); 56 | } else { 57 | eprintln!("Failed."); 58 | } 59 | } else { 60 | eprintln!("Interrupted!"); 61 | } 62 | ``` 63 | 64 | ### Execute and Get the Output 65 | 66 | #### Output to the Screen 67 | 68 | ```rust,ignore 69 | use std::process::Command; 70 | 71 | use execute::Execute; 72 | 73 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 74 | 75 | let mut command = Command::new(FFMPEG_PATH); 76 | 77 | command.arg("-i"); 78 | command.arg("/path/to/media-file"); 79 | command.arg("/path/to/output-file"); 80 | 81 | let output = command.execute_output().unwrap(); 82 | 83 | if let Some(exit_code) = output.status.code() { 84 | if exit_code == 0 { 85 | println!("Ok."); 86 | } else { 87 | eprintln!("Failed."); 88 | } 89 | } else { 90 | eprintln!("Interrupted!"); 91 | } 92 | ``` 93 | 94 | #### Output to Memory (Captured) 95 | 96 | ```rust,ignore 97 | use std::process::{Command, Stdio}; 98 | 99 | use execute::Execute; 100 | 101 | const FFMPEG_PATH: &str = "/path/to/ffmpeg"; 102 | 103 | let mut command = Command::new(FFMPEG_PATH); 104 | 105 | command.arg("-i"); 106 | command.arg("/path/to/media-file"); 107 | command.arg("/path/to/output-file"); 108 | 109 | command.stdout(Stdio::piped()); 110 | command.stderr(Stdio::piped()); 111 | 112 | let output = command.execute_output().unwrap(); 113 | 114 | if let Some(exit_code) = output.status.code() { 115 | if exit_code == 0 { 116 | println!("Ok."); 117 | } else { 118 | eprintln!("Failed."); 119 | } 120 | } else { 121 | eprintln!("Interrupted!"); 122 | } 123 | 124 | println!("{}", String::from_utf8(output.stdout).unwrap()); 125 | println!("{}", String::from_utf8(output.stderr).unwrap()); 126 | ``` 127 | 128 | ### Execute and Input Data 129 | 130 | #### Input In-memory Data 131 | 132 | ```rust 133 | use std::process::{Command, Stdio}; 134 | 135 | use execute::Execute; 136 | 137 | # if cfg!(target_os = "linux") { 138 | let mut bc_command = Command::new("bc"); 139 | 140 | bc_command.stdout(Stdio::piped()); 141 | 142 | let output = bc_command.execute_input_output("2^99\n").unwrap(); 143 | 144 | println!("Answer: {}", String::from_utf8(output.stdout).unwrap().trim_end()); 145 | # } 146 | ``` 147 | 148 | #### Input from a Reader 149 | 150 | ```rust 151 | use std::process::{Command, Stdio}; 152 | use std::fs::File; 153 | 154 | use execute::Execute; 155 | 156 | # if cfg!(target_os = "linux") { 157 | let mut cat_command = Command::new("cat"); 158 | 159 | cat_command.stdout(Stdio::piped()); 160 | 161 | let mut file = File::open("Cargo.toml").unwrap(); 162 | 163 | let output = cat_command.execute_input_reader_output(&mut file).unwrap(); 164 | 165 | println!("{}", String::from_utf8(output.stdout).unwrap()); 166 | # } 167 | ``` 168 | 169 | By default, the buffer size is 256 bytes. If you want to change that, you can use the `_reader_output2` or `_reader2` methods and define a length explicitly. 170 | 171 | For example, to change the buffer size to 4096 bytes, 172 | 173 | ```rust 174 | use std::process::{Command, Stdio}; 175 | use std::fs::File; 176 | 177 | use execute::generic_array::typenum::U4096; 178 | use execute::Execute; 179 | 180 | # if cfg!(target_os = "linux") { 181 | let mut cat_command = Command::new("cat"); 182 | 183 | cat_command.stdout(Stdio::piped()); 184 | 185 | let mut file = File::open("Cargo.toml").unwrap(); 186 | 187 | let output = cat_command.execute_input_reader_output2::(&mut file).unwrap(); 188 | 189 | println!("{}", String::from_utf8(output.stdout).unwrap()); 190 | # } 191 | ``` 192 | 193 | ### Execute Multiple Commands and Pipe Them Together 194 | 195 | ```rust 196 | use std::process::{Command, Stdio}; 197 | 198 | use execute::Execute; 199 | 200 | # if cfg!(target_os = "linux") { 201 | let mut command1 = Command::new("echo"); 202 | command1.arg("HELLO WORLD"); 203 | 204 | let mut command2 = Command::new("cut"); 205 | command2.arg("-d").arg(" ").arg("-f").arg("1"); 206 | 207 | let mut command3 = Command::new("tr"); 208 | command3.arg("A-Z").arg("a-z"); 209 | 210 | command3.stdout(Stdio::piped()); 211 | 212 | let output = command1.execute_multiple_output(&mut [&mut command2, &mut command3]).unwrap(); 213 | 214 | assert_eq!(b"hello\n", output.stdout.as_slice()); 215 | # } 216 | ``` 217 | 218 | ### Run a Command String in the Current Shell 219 | 220 | The `shell` function can be used to create a `Command` instance with a single command string instead of a program name and scattered arguments. 221 | 222 | ```rust 223 | use std::process::{Command, Stdio}; 224 | 225 | use execute::{Execute, shell}; 226 | 227 | # if cfg!(target_os = "linux") { 228 | let mut command = shell("cat /proc/meminfo"); 229 | 230 | command.stdout(Stdio::piped()); 231 | 232 | let output = command.execute_output().unwrap(); 233 | 234 | println!("{}", String::from_utf8(output.stdout).unwrap()); 235 | # } 236 | ``` 237 | 238 | ### Parse a Command String at Runtime 239 | 240 | The `command` function can be used to create a `Command` instance with a single command string instead of a program name and scattered arguments. The difference between the `shell` function and the `command` function is that the former is interpreted by the current shell while the latter is parsed by this crate. 241 | 242 | ```rust 243 | use std::process::{Command, Stdio}; 244 | 245 | use execute::{Execute, command}; 246 | 247 | # if cfg!(target_os = "linux") { 248 | let mut command = command("cat '/proc/meminfo'"); 249 | 250 | command.stdout(Stdio::piped()); 251 | 252 | let output = command.execute_output().unwrap(); 253 | 254 | println!("{}", String::from_utf8(output.stdout).unwrap()); 255 | # } 256 | ``` 257 | 258 | ### Parse a Command String at Compile Time 259 | 260 | The `command!` macro can be used to create a `Command` instance with a single command string literal instead of a program name and scattered arguments. 261 | 262 | ```rust 263 | use std::process::{Command, Stdio}; 264 | 265 | use execute::Execute; 266 | 267 | # if cfg!(target_os = "linux") { 268 | let mut command = execute::command!("cat '/proc/meminfo'"); 269 | 270 | command.stdout(Stdio::piped()); 271 | 272 | let output = command.execute_output().unwrap(); 273 | 274 | println!("{}", String::from_utf8(output.stdout).unwrap()); 275 | # } 276 | ``` 277 | 278 | ### Create a `Command` Instance by Providing Arguments Separately 279 | 280 | The `command_args!` macro can be used to create a `Command` instance with a program name and arguments separately. The program name and arguments can be non-literal. 281 | 282 | ```rust 283 | use std::process::{Command, Stdio}; 284 | 285 | use execute::Execute; 286 | 287 | # if cfg!(target_os = "linux") { 288 | let mut command = execute::command_args!("cat", "/proc/meminfo"); 289 | 290 | command.stdout(Stdio::piped()); 291 | 292 | let output = command.execute_output().unwrap(); 293 | 294 | println!("{}", String::from_utf8(output.stdout).unwrap()); 295 | # } 296 | ``` 297 | */ 298 | 299 | pub extern crate generic_array; 300 | 301 | #[cfg(unix)] 302 | use std::{env, ffi::OsString}; 303 | use std::{ 304 | ffi::OsStr, 305 | io::{self, ErrorKind, Read, Write}, 306 | process::{Command, Output, Stdio}, 307 | }; 308 | 309 | pub use execute_command_macro::{command, command_args}; 310 | use execute_command_tokens::command_tokens; 311 | use generic_array::{ 312 | typenum::{IsGreaterOrEqual, True, U1, U256}, 313 | ArrayLength, GenericArray, 314 | }; 315 | 316 | pub trait Execute { 317 | /// Execute this command and get the exit status code. stdout and stderr will be set to `Stdio::null()`. By default, stdin is inherited from the parent. 318 | fn execute(&mut self) -> Result, io::Error>; 319 | 320 | /// Execute this command and get the exit status code. By default, stdin, stdout and stderr are inherited from the parent. 321 | fn execute_output(&mut self) -> Result; 322 | 323 | /// Execute this command and check the exit status code. stdout and stderr will be set to `Stdio::null()`. By default, stdin is inherited from the parent. It's usually used for checking whether the program is correct. 324 | #[inline] 325 | fn execute_check_exit_status_code( 326 | &mut self, 327 | expected_exit_status_code: i32, 328 | ) -> Result<(), io::Error> { 329 | match self.execute()? { 330 | Some(exit_status_code) if exit_status_code == expected_exit_status_code => Ok(()), 331 | _ => Err(io::Error::other("unexpected exit status")), 332 | } 333 | } 334 | 335 | /// Execute this command and input in-memory data to the process. stdin will be set to `Stdio::piped()`. stdout and stderr will be set to `Stdio::null()`. 336 | fn execute_input>( 337 | &mut self, 338 | data: &D, 339 | ) -> Result, io::Error>; 340 | 341 | /// Execute this command and input in-memory data to the process. stdin will be set to `Stdio::piped()`. By default, stdout and stderr are inherited from the parent. 342 | fn execute_input_output>( 343 | &mut self, 344 | data: &D, 345 | ) -> Result; 346 | 347 | /// Execute this command and input data from a reader to the process. stdin will be set to `Stdio::piped()`. stdout and stderr will be set to `Stdio::null()`. 348 | #[inline] 349 | fn execute_input_reader(&mut self, reader: &mut dyn Read) -> Result, io::Error> { 350 | self.execute_input_reader2::(reader) 351 | } 352 | 353 | /// Execute this command and input data from a reader to the process. stdin will be set to `Stdio::piped()`. stdout and stderr will be set to `Stdio::null()`. 354 | fn execute_input_reader2>( 355 | &mut self, 356 | reader: &mut dyn Read, 357 | ) -> Result, io::Error>; 358 | 359 | /// Execute this command and input data from a reader to the process. stdin will be set to `Stdio::piped()`. By default, stdout and stderr are inherited from the parent. 360 | #[inline] 361 | fn execute_input_reader_output(&mut self, reader: &mut dyn Read) -> Result { 362 | self.execute_input_reader_output2::(reader) 363 | } 364 | 365 | /// Execute this command and input data from a reader to the process. stdin will be set to `Stdio::piped()`. By default, stdout and stderr are inherited from the parent. 366 | fn execute_input_reader_output2>( 367 | &mut self, 368 | reader: &mut dyn Read, 369 | ) -> Result; 370 | 371 | // TODO execute_multiple 372 | 373 | /// Execute this command as well as other commands and pipe their stdin and stdout, and get the exit status code. The stdout and stderr of the last process will be set to `Stdio::null()`. By default, the stdin of the first process is inherited from the parent. 374 | fn execute_multiple(&mut self, others: &mut [&mut Command]) -> Result, io::Error>; 375 | 376 | /// Execute this command as well as other commands and pipe their stdin and stdout. By default, the stdin of the first process, the stdout and stderr of the last process are inherited from the parent. 377 | fn execute_multiple_output(&mut self, others: &mut [&mut Command]) 378 | -> Result; 379 | 380 | /// Execute this command as well as other commands and pipe their stdin and stdout, and input in-memory data to the process, and get the exit status code. The stdin of the first process will be set to `Stdio::piped()`. The stdout and stderr of the last process will be set to `Stdio::null()`. 381 | fn execute_multiple_input>( 382 | &mut self, 383 | data: &D, 384 | others: &mut [&mut Command], 385 | ) -> Result, io::Error>; 386 | 387 | /// Execute this command as well as other commands and pipe their stdin and stdout, and input in-memory data to the process. The stdin of the first process will be set to `Stdio::piped()`. By default, the stdout and stderr of the last process are inherited from the parent. 388 | fn execute_multiple_input_output>( 389 | &mut self, 390 | data: &D, 391 | others: &mut [&mut Command], 392 | ) -> Result; 393 | 394 | /// Execute this command as well as other commands and pipe their stdin and stdout, and input data from a reader to the process, and get the exit status code. The stdin of the first process will be set to `Stdio::piped()`. The stdout and stderr of the last process will be set to `Stdio::null()`. 395 | #[inline] 396 | fn execute_multiple_input_reader( 397 | &mut self, 398 | reader: &mut dyn Read, 399 | others: &mut [&mut Command], 400 | ) -> Result, io::Error> { 401 | self.execute_multiple_input_reader2::(reader, others) 402 | } 403 | 404 | /// Execute this command as well as other commands and pipe their stdin and stdout, and input data from a reader to the process, and get the exit status code. The stdin of the first process will be set to `Stdio::piped()`. The stdout and stderr of the last process will be set to `Stdio::null()`. 405 | fn execute_multiple_input_reader2>( 406 | &mut self, 407 | reader: &mut dyn Read, 408 | others: &mut [&mut Command], 409 | ) -> Result, io::Error>; 410 | 411 | /// Execute this command as well as other commands and pipe their stdin and stdout, and input data from a reader to the process. The stdin of the first process will be set to `Stdio::piped()`. By default, the stdout and stderr of the last process are inherited from the parent. 412 | #[inline] 413 | fn execute_multiple_input_reader_output( 414 | &mut self, 415 | reader: &mut dyn Read, 416 | others: &mut [&mut Command], 417 | ) -> Result { 418 | self.execute_multiple_input_reader_output2::(reader, others) 419 | } 420 | 421 | /// Execute this command as well as other commands and pipe their stdin and stdout, and input data from a reader to the process. The stdin of the first process will be set to `Stdio::piped()`. By default, the stdout and stderr of the last process are inherited from the parent. 422 | fn execute_multiple_input_reader_output2>( 423 | &mut self, 424 | reader: &mut dyn Read, 425 | others: &mut [&mut Command], 426 | ) -> Result; 427 | } 428 | 429 | impl Execute for Command { 430 | #[inline] 431 | fn execute(&mut self) -> Result, io::Error> { 432 | self.stdout(Stdio::null()); 433 | self.stderr(Stdio::null()); 434 | 435 | Ok(self.status()?.code()) 436 | } 437 | 438 | #[inline] 439 | fn execute_output(&mut self) -> Result { 440 | self.spawn()?.wait_with_output() 441 | } 442 | 443 | #[inline] 444 | fn execute_input>( 445 | &mut self, 446 | data: &D, 447 | ) -> Result, io::Error> { 448 | self.stdin(Stdio::piped()); 449 | self.stdout(Stdio::null()); 450 | self.stderr(Stdio::null()); 451 | 452 | let mut child = self.spawn()?; 453 | 454 | child.stdin.as_mut().unwrap().write_all(data.as_ref())?; 455 | 456 | Ok(child.wait()?.code()) 457 | } 458 | 459 | #[inline] 460 | fn execute_input_output>( 461 | &mut self, 462 | data: &D, 463 | ) -> Result { 464 | self.stdin(Stdio::piped()); 465 | 466 | let mut child = self.spawn()?; 467 | 468 | child.stdin.as_mut().unwrap().write_all(data.as_ref())?; 469 | 470 | child.wait_with_output() 471 | } 472 | 473 | #[inline] 474 | fn execute_input_reader2>( 475 | &mut self, 476 | reader: &mut dyn Read, 477 | ) -> Result, io::Error> { 478 | self.stdin(Stdio::piped()); 479 | self.stdout(Stdio::null()); 480 | self.stderr(Stdio::null()); 481 | 482 | let mut child = self.spawn()?; 483 | 484 | { 485 | let stdin = child.stdin.as_mut().unwrap(); 486 | 487 | let mut buffer: GenericArray = GenericArray::default(); 488 | 489 | loop { 490 | match reader.read(&mut buffer) { 491 | Ok(0) => break, 492 | Ok(c) => stdin.write_all(&buffer[0..c])?, 493 | Err(ref err) if err.kind() == ErrorKind::Interrupted => (), 494 | Err(err) => return Err(err), 495 | } 496 | } 497 | } 498 | 499 | Ok(child.wait()?.code()) 500 | } 501 | 502 | #[inline] 503 | fn execute_input_reader_output2>( 504 | &mut self, 505 | reader: &mut dyn Read, 506 | ) -> Result { 507 | self.stdin(Stdio::piped()); 508 | 509 | let mut child = self.spawn()?; 510 | 511 | { 512 | let stdin = child.stdin.as_mut().unwrap(); 513 | 514 | let mut buffer: GenericArray = GenericArray::default(); 515 | 516 | loop { 517 | match reader.read(&mut buffer) { 518 | Ok(0) => break, 519 | Ok(c) => stdin.write_all(&buffer[0..c])?, 520 | Err(ref err) if err.kind() == ErrorKind::Interrupted => (), 521 | Err(err) => return Err(err), 522 | } 523 | } 524 | } 525 | 526 | child.wait_with_output() 527 | } 528 | 529 | fn execute_multiple(&mut self, others: &mut [&mut Command]) -> Result, io::Error> { 530 | if others.is_empty() { 531 | return self.execute(); 532 | } 533 | 534 | self.stdout(Stdio::piped()); 535 | self.stderr(Stdio::null()); 536 | 537 | let mut child = self.spawn()?; 538 | 539 | let others_length_dec = others.len() - 1; 540 | 541 | for other in others.iter_mut().take(others_length_dec) { 542 | other.stdin(child.stdout.unwrap()); 543 | other.stdout(Stdio::piped()); 544 | other.stderr(Stdio::null()); 545 | 546 | child = other.spawn()?; 547 | } 548 | 549 | let last_other = &mut others[others_length_dec]; 550 | 551 | last_other.stdin(child.stdout.unwrap()); 552 | last_other.stdout(Stdio::null()); 553 | last_other.stderr(Stdio::null()); 554 | 555 | Ok(last_other.status()?.code()) 556 | } 557 | 558 | fn execute_multiple_output( 559 | &mut self, 560 | others: &mut [&mut Command], 561 | ) -> Result { 562 | if others.is_empty() { 563 | return self.execute_output(); 564 | } 565 | 566 | self.stdout(Stdio::piped()); 567 | self.stderr(Stdio::null()); 568 | 569 | let mut child = self.spawn()?; 570 | 571 | let others_length_dec = others.len() - 1; 572 | 573 | for other in others.iter_mut().take(others_length_dec) { 574 | other.stdin(child.stdout.unwrap()); 575 | other.stdout(Stdio::piped()); 576 | other.stderr(Stdio::null()); 577 | 578 | child = other.spawn()?; 579 | } 580 | 581 | let last_other = &mut others[others_length_dec]; 582 | 583 | last_other.stdin(child.stdout.unwrap()); 584 | 585 | last_other.spawn()?.wait_with_output() 586 | } 587 | 588 | fn execute_multiple_input>( 589 | &mut self, 590 | data: &D, 591 | others: &mut [&mut Command], 592 | ) -> Result, io::Error> { 593 | if others.is_empty() { 594 | return self.execute_input(data); 595 | } 596 | 597 | self.stdin(Stdio::piped()); 598 | self.stdout(Stdio::piped()); 599 | self.stderr(Stdio::null()); 600 | 601 | let mut child = self.spawn()?; 602 | 603 | child.stdin.as_mut().unwrap().write_all(data.as_ref())?; 604 | 605 | let others_length_dec = others.len() - 1; 606 | 607 | for other in others.iter_mut().take(others_length_dec) { 608 | other.stdin(child.stdout.unwrap()); 609 | other.stdout(Stdio::piped()); 610 | other.stderr(Stdio::null()); 611 | 612 | child = other.spawn()?; 613 | } 614 | 615 | let last_other = &mut others[others_length_dec]; 616 | 617 | last_other.stdin(child.stdout.unwrap()); 618 | last_other.stdout(Stdio::null()); 619 | last_other.stderr(Stdio::null()); 620 | 621 | Ok(last_other.status()?.code()) 622 | } 623 | 624 | fn execute_multiple_input_output>( 625 | &mut self, 626 | data: &D, 627 | others: &mut [&mut Command], 628 | ) -> Result { 629 | if others.is_empty() { 630 | return self.execute_input_output(data); 631 | } 632 | 633 | self.stdin(Stdio::piped()); 634 | self.stdout(Stdio::piped()); 635 | self.stderr(Stdio::null()); 636 | 637 | let mut child = self.spawn()?; 638 | 639 | child.stdin.as_mut().unwrap().write_all(data.as_ref())?; 640 | 641 | let others_length_dec = others.len() - 1; 642 | 643 | for other in others.iter_mut().take(others_length_dec) { 644 | other.stdin(child.stdout.unwrap()); 645 | other.stdout(Stdio::piped()); 646 | other.stderr(Stdio::null()); 647 | 648 | child = other.spawn()?; 649 | } 650 | 651 | let last_other = &mut others[others_length_dec]; 652 | 653 | last_other.stdin(child.stdout.unwrap()); 654 | 655 | last_other.spawn()?.wait_with_output() 656 | } 657 | 658 | fn execute_multiple_input_reader2>( 659 | &mut self, 660 | reader: &mut dyn Read, 661 | others: &mut [&mut Command], 662 | ) -> Result, io::Error> { 663 | if others.is_empty() { 664 | return self.execute_input_reader2::(reader); 665 | } 666 | 667 | self.stdin(Stdio::piped()); 668 | self.stdout(Stdio::piped()); 669 | self.stderr(Stdio::null()); 670 | 671 | let mut child = self.spawn()?; 672 | 673 | { 674 | let stdin = child.stdin.as_mut().unwrap(); 675 | 676 | let mut buffer: GenericArray = GenericArray::default(); 677 | 678 | loop { 679 | match reader.read(&mut buffer) { 680 | Ok(0) => break, 681 | Ok(c) => stdin.write_all(&buffer[0..c])?, 682 | Err(ref err) if err.kind() == ErrorKind::Interrupted => (), 683 | Err(err) => return Err(err), 684 | } 685 | } 686 | } 687 | 688 | let others_length_dec = others.len() - 1; 689 | 690 | for other in others.iter_mut().take(others_length_dec) { 691 | other.stdin(child.stdout.unwrap()); 692 | other.stdout(Stdio::piped()); 693 | other.stderr(Stdio::null()); 694 | 695 | child = other.spawn()?; 696 | } 697 | 698 | let last_other = &mut others[others_length_dec]; 699 | 700 | last_other.stdin(child.stdout.unwrap()); 701 | last_other.stdout(Stdio::null()); 702 | last_other.stderr(Stdio::null()); 703 | 704 | Ok(last_other.status()?.code()) 705 | } 706 | 707 | fn execute_multiple_input_reader_output2< 708 | N: ArrayLength + IsGreaterOrEqual, 709 | >( 710 | &mut self, 711 | reader: &mut dyn Read, 712 | others: &mut [&mut Command], 713 | ) -> Result { 714 | if others.is_empty() { 715 | return self.execute_input_reader_output2::(reader); 716 | } 717 | 718 | self.stdin(Stdio::piped()); 719 | self.stdout(Stdio::piped()); 720 | self.stderr(Stdio::null()); 721 | 722 | let mut child = self.spawn()?; 723 | 724 | { 725 | let stdin = child.stdin.as_mut().unwrap(); 726 | 727 | let mut buffer: GenericArray = GenericArray::default(); 728 | 729 | loop { 730 | match reader.read(&mut buffer) { 731 | Ok(0) => break, 732 | Ok(c) => stdin.write_all(&buffer[0..c])?, 733 | Err(ref err) if err.kind() == ErrorKind::Interrupted => (), 734 | Err(err) => return Err(err), 735 | } 736 | } 737 | } 738 | 739 | let others_length_dec = others.len() - 1; 740 | 741 | for other in others.iter_mut().take(others_length_dec) { 742 | other.stdin(child.stdout.unwrap()); 743 | other.stdout(Stdio::piped()); 744 | other.stderr(Stdio::null()); 745 | 746 | child = other.spawn()?; 747 | } 748 | 749 | let last_other = &mut others[others_length_dec]; 750 | 751 | last_other.stdin(child.stdout.unwrap()); 752 | 753 | last_other.spawn()?.wait_with_output() 754 | } 755 | } 756 | 757 | /// Create a `Command` instance which can be executed by the current command language interpreter (shell). 758 | #[cfg(unix)] 759 | #[inline] 760 | pub fn shell>(cmd: S) -> Command { 761 | use std::sync::LazyLock; 762 | 763 | static SHELL: LazyLock = LazyLock::new(|| { 764 | env::var_os("SHELL").unwrap_or_else(|| OsString::from(String::from("sh"))) 765 | }); 766 | 767 | let mut command = Command::new(&*SHELL); 768 | 769 | command.arg("-c"); 770 | command.arg(cmd); 771 | 772 | command 773 | } 774 | 775 | /// Create a `Command` instance which can be executed by the current command language interpreter (shell). 776 | #[cfg(windows)] 777 | #[inline] 778 | pub fn shell>(cmd: S) -> Command { 779 | let mut command = Command::new("cmd.exe"); 780 | 781 | command.arg("/c"); 782 | command.arg(cmd); 783 | 784 | command 785 | } 786 | 787 | /// Create a `Command` instance by parsing a command string. 788 | #[inline] 789 | pub fn command>(cmd: S) -> Command { 790 | let tokens = command_tokens(cmd); 791 | 792 | if tokens.is_empty() { 793 | Command::new("") 794 | } else { 795 | let mut command = Command::new(&tokens[0]); 796 | 797 | command.args(&tokens[1..]); 798 | 799 | command 800 | } 801 | } 802 | --------------------------------------------------------------------------------