diff --git a/src/lib.rs b/src/lib.rs index 0f2b63a..9dbea0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ use clap::{Arg, ArgAction, Command}; use std::ffi::{OsStr, OsString}; use std::io::{IsTerminal as _, Read}; use std::path::Path; -use uucore::error::{FromIo, UResult, USimpleError}; +use uucore::error::{ExitCode, FromIo, UResult, USimpleError}; #[derive(Clone, Copy, PartialEq, Eq)] #[doc(hidden)] @@ -399,6 +399,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; let matcher = Matcher::compile(&config)?; + // An empty pattern matches every line; with `-v`, GNU grep selects no lines + // and exits as "no match" without reading any input files. + if invert_match && patterns.iter().any(|pattern| pattern.is_empty()) { + return Err(ExitCode::new(1)); + } + let writer = OutputWriter::new(&config); let mut searcher = Searcher::new(&config, matcher, writer); let mut lb = LineBuffer::new(if config.null_data { b'\0' } else { b'\n' }); diff --git a/tests/test_grep.rs b/tests/test_grep.rs index d061918..ae03f70 100644 --- a/tests/test_grep.rs +++ b/tests/test_grep.rs @@ -374,6 +374,27 @@ fn empty_pattern_matches_every_line() { .stdout_only("a\nb\nc\n"); } +#[test] +fn inverted_empty_pattern_short_circuits() { + let (_s, mut c) = ucmd(); + c.args(&["-e", "", "-v", "-c"]) + .pipe_in("a\nb\n") + .fails_with_code(1) + .no_output(); + + let (_s, mut c) = ucmd(); + c.args(&["-e", "", "-v", "-c", "missing"]) + .fails_with_code(1) + .no_output(); + + let (_s, mut c) = ucmd(); + c.args(&["-e", "", "-e", "[", "-v"]) + .pipe_in("a\n") + .fails_with_code(2) + .no_stdout() + .stderr_contains("invalid pattern"); +} + #[test] fn pattern_starting_with_dash_needs_double_dash() { let (_s, mut c) = ucmd();