diff --git a/src/lib.rs b/src/lib.rs index b2b75bd..b915ae0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -421,6 +421,10 @@ pub fn uu_app() -> Command { .about("Search for PATTERNS in each FILE.") .disable_help_flag(true) .disable_version_flag(true) + // GNU grep accepts repeated options (booleans are idempotent, value + // options take the last); make clap replace rather than error. Args + // with ArgAction::Append (e.g. -e/-f/--include) still accumulate. + .args_override_self(true) .after_help( "When FILE is '-', read standard input. If no FILE is given, read standard \ input, but with -r, recursively search the working directory instead. With \ diff --git a/tests/test_grep.rs b/tests/test_grep.rs index 573ca03..df629f2 100644 --- a/tests/test_grep.rs +++ b/tests/test_grep.rs @@ -1138,3 +1138,38 @@ fn help_and_version() { .succeeds() .stdout_contains(env!("CARGO_PKG_VERSION")); } + +#[test] +fn repeated_options_are_accepted() { + // GNU grep tolerates options given more than once: boolean flags are + // idempotent and value options take the last occurrence. clap would + // otherwise error with "cannot be used multiple times". + + // Repeated boolean flags are a no-op (not an error). + let (_s, mut c) = ucmd(); + c.args(&["-n", "-n", "a"]) + .pipe_in("abc\n") + .succeeds() + .stdout_only("1:abc\n"); + + // Mixed repeated booleans behave like a single occurrence. + let (_s, mut c) = ucmd(); + c.args(&["-i", "-i", "abc"]) + .pipe_in("ABC\n") + .succeeds() + .stdout_only("ABC\n"); + + // Repeated value options take the last value (here: -m 1 wins). + let (_s, mut c) = ucmd(); + c.args(&["-m", "5", "-m", "1", "x"]) + .pipe_in("x\nx\nx\n") + .succeeds() + .stdout_only("x\n"); + + // -e (ArgAction::Append) must still accumulate every pattern. + let (_s, mut c) = ucmd(); + c.args(&["-e", "a", "-e", "b"]) + .pipe_in("a\nb\nc\n") + .succeeds() + .stdout_only("a\nb\n"); +}