When -o (only-matching) and -v (invert) are combined with a pattern whose only match on some line is zero-width (e.g. $, x*), and that line is empty, uu_grep treats the empty line as not matching. -v then selects it, so uu_grep reports a selection and exits 0. GNU matches the zero-width pattern on the empty line (so -v does not select it) and exits 1.
Found by the differential fuzzer (fuzz_grep). This shares its root with #18 (zero-length matches are dropped before they can mark a line as matched), but manifests through the -o/-v path and the exit code rather than -w/-x.
Input is a, an empty line, b — the pattern $ matches the end of all three lines.
Rust (incorrect)
$ printf 'a\n\nb\n' | ./target/release/grep -e '$' -v -o
# Output: (none)
# Exit code: 0
GNU (correct)
$ printf 'a\n\nb\n' | LC_ALL=C /usr/bin/grep -e '$' -v -o
# Output: (none)
# Exit code: 1
Same with x* instead of $. Controls that agree: without -o (grep '$' -v) both exit 1; without the empty line (printf 'a\nb\n') both exit 1; a plain count grep '$' -c is 3 in both (so the empty line does match in plain mode — only the -o path mishandles it).
When
-o(only-matching) and-v(invert) are combined with a pattern whose only match on some line is zero-width (e.g.$,x*), and that line is empty,uu_greptreats the empty line as not matching.-vthen selects it, souu_grepreports a selection and exits0. GNU matches the zero-width pattern on the empty line (so-vdoes not select it) and exits1.Found by the differential fuzzer (
fuzz_grep). This shares its root with #18 (zero-length matches are dropped before they can mark a line as matched), but manifests through the-o/-vpath and the exit code rather than-w/-x.Input is
a, an empty line,b— the pattern$matches the end of all three lines.Rust (incorrect)
GNU (correct)
Same with
x*instead of$. Controls that agree: without-o(grep '$' -v) both exit 1; without the empty line (printf 'a\nb\n') both exit 1; a plain countgrep '$' -cis3in both (so the empty line does match in plain mode — only the-opath mishandles it).