Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass a single string with multiple arguments to std::process::Command?

Rust's std::process::Command type demands that process arguments be passed in individually via .arg("-arg1").arg("-arg2") or as a vector of strings via .args(&["-arg1", "-arg2"]). How do you split a string into a vector that can be passed as arguments?

like image 255
Petrus Theron Avatar asked Nov 27 '18 11:11

Petrus Theron


1 Answers

What shells do when splitting a command-line string into arguments is far from trivial, especially when you want to handle things like quoting. For example, your code should pass the following assertions:

assert_eq!(split(r#""foo\"bar""#), vec!["foo\"bar"]);
assert_eq!(split(r#""foo"#), vec!["\"foo"]);          // Or error

Unless you think simply splitting on whitespace is enough for your use-case, you should really use a crate like shell-words or shlex. shlex presents the advantage that it can return an iterator, thus avoiding unnecessary allocations, but on the other hand it makes it easy to miss the error in the second test above:

extern crate shell_words;
extern crate shlex;

use shell_words::split;
use shlex::Shlex;

fn main() {
    assert_eq!(split(r#"a b"#).unwrap(), vec!["a", "b"]);
    let mut lex = Shlex::new(r#"a b"#);
    assert_eq!(lex.by_ref().collect::<Vec<_>>(), vec!["a", "b"]);
    assert!(!lex.had_error);    // ← Don't forget this check!

    assert_eq!(split(r#"a "b c""#).unwrap(), vec!["a", "b c"]);
    let mut lex = Shlex::new(r#"a "b c""#);
    assert_eq!(lex.by_ref().collect::<Vec<_>>(), vec!["a", "b c"]);
    assert!(!lex.had_error);    // ← Don't forget this check!

    assert_eq!(split(r#""foo\"bar""#).unwrap(), vec!["foo\"bar"]);
    let mut lex = Shlex::new(r#""foo\"bar""#);
    assert_eq!(lex.by_ref().collect::<Vec<_>>(), vec!["foo\"bar"]);
    assert!(!lex.had_error);    // ← Don't forget this check!

    assert!(split(r#""foo"#).is_err());
    // assert_eq!(Shlex::new(r#""foo"#).collect::<Vec<_>>(), vec!["\"foo"]);

    let mut lex = Shlex::new(r#""foo"#);
    lex.by_ref().for_each (drop);
    assert!(lex.had_error);     // ← Don't forget this check!
}
like image 118
Jmb Avatar answered Nov 02 '22 09:11

Jmb