From 399d2d1192fa3df8778af9a5d5ebcb43a66f92af Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 30 May 2026 18:09:16 +0200 Subject: [PATCH] grep: -q match yields exit 0 even after a file error GNU grep documents that with -q the exit status is 0 as soon as a line is selected, even if an error (such as a missing file) occurred. uu_grep let had_error take precedence, so 'grep -q PAT missing -' returned 2 instead of 0 when stdin matched. Give quiet+match priority in finish(); without -q, or with -q and no match, a file error still yields exit 2. Fixes the GNU testsuite 'status' test. --- src/searcher.rs | 7 ++++++- tests/test_grep.rs | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/searcher.rs b/src/searcher.rs index c63c826..bcee67d 100644 --- a/src/searcher.rs +++ b/src/searcher.rs @@ -117,7 +117,12 @@ impl<'a> Searcher<'a> { .flush() .map_err_context(|| "(standard output)".to_string())?; - if self.had_error { + // With -q, a match yields exit status 0 even if an error (e.g. a + // missing file) occurred earlier: GNU exits as soon as a line is + // selected, so the error never affects the status. + if self.config.quiet && self.any_match { + Ok(()) + } else if self.had_error { Err(ExitCode::new(2)) } else if self.any_match { Ok(()) diff --git a/tests/test_grep.rs b/tests/test_grep.rs index 5e487ea..20e77bc 100644 --- a/tests/test_grep.rs +++ b/tests/test_grep.rs @@ -126,6 +126,28 @@ fn ere_invalid_pattern_is_error() { .stderr_contains("invalid pattern"); } +#[test] +fn quiet_match_overrides_file_error() { + // With -q, a match makes grep exit 0 even if an earlier file could not be + // opened. Without -q the missing file still yields exit 2, and -q with no + // match keeps the error status. + let (_s, mut c) = ucmd(); + c.args(&["-q", "abc", "no-such-file", "-"]) + .pipe_in("abcd\n") + .succeeds() + .no_output(); + + let (_s, mut c) = ucmd(); + c.args(&["abc", "no-such-file", "-"]) + .pipe_in("abcd\n") + .fails_with_code(2); + + let (_s, mut c) = ucmd(); + c.args(&["-q", "zzz", "no-such-file", "-"]) + .pipe_in("abcd\n") + .fails_with_code(2); +} + #[test] fn fixed_string_is_literal() { // Metacharacters are not interpreted.