Skip to content

fix(grep): use portable --null in system grep fallback (BSD/macOS)#2405

Merged
KuSh merged 1 commit into
rtk-ai:developfrom
vegetablechicken233:fix/grep-bsd-null-fallback
Jun 16, 2026
Merged

fix(grep): use portable --null in system grep fallback (BSD/macOS)#2405
KuSh merged 1 commit into
rtk-ai:developfrom
vegetablechicken233:fix/grep-bsd-null-fallback

Conversation

@vegetablechicken233

Copy link
Copy Markdown
Contributor

Problem

On macOS/BSD systems without ripgrep installed, rtk grep prints N matches in 0 files: followed by [+N more] — the match count is right, but zero matched lines are shown, with exit code 0. Reported in #2310 (see also #2387).

Repro on a default macOS install (no rg on PATH):

printf 'alpha one\nbeta two\nalpha three\n' > a.txt
rtk grep -n "alpha" a.txt
# → 2 matches in 0 files:
#   [+2 more]

Root cause

When rg is unavailable, the fallback runs grep -rnHZ <pattern> <path>, relying on -Z to NUL-separate the filename — which is what parse_match_line() hard-requires (^([^\x00]+)\x00(\d+):(.*)$, introduced for #1436).

-Z only means --null on GNU grep. On BSD/macOS grep, -Z is an alias for --decompress (zgrep mode), so output is plain file:line:content with no NUL byte. Every line then fails to parse: total_matches counts raw lines (correct count), but by_file stays empty → "0 files", and all content is suppressed into [+N more]. The bug is invisible on Linux and silently breaks every default macOS install without ripgrep.

Fix

Use the long option --null, which GNU and BSD grep both define as "print a zero-byte after the file name" (verified against both man pages). The fallback flags are extracted into a GREP_FALLBACK_FLAGS constant so tests can assert portability.

Tests

  • test_grep_fallback_uses_portable_null_flag — regression guard: asserts --null is used, -Z is not, and flags stay un-bundled (so a future -rnHZ-style bundle can't sneak -Z back in).
  • test_grep_fallback_output_is_nul_separated_and_parseable — runs the real system grep with the fallback flags against a fixture and asserts the output contains NUL and parses via parse_match_line(). Skips gracefully if grep is absent.

Verified locally on macOS (BSD grep, Apple Silicon): cargo fmt --all && cargo clippy --all-targets && cargo test --all → clippy clean, 2154 tests pass, including both new tests against the actual BSD grep this bug ships on.

Related to #2310: that report says non-recursive rtk grep breaks while -r works, which suggests the reporter may have a partial-rg environment — but the "N matches in 0 files + hidden lines" symptom is produced exactly and only by NUL-less underlying output, and this fixes the macOS no-ripgrep case of it.

🤖 Generated with Claude Code

@CLAassistant

CLAassistant commented Jun 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@vegetablechicken233 vegetablechicken233 force-pushed the fix/grep-bsd-null-fallback branch 2 times, most recently from 45de9a5 to 1c97cea Compare June 15, 2026 09:31
@vegetablechicken233

Copy link
Copy Markdown
Contributor Author

Hi! Just rebased this onto the latest develop after the great grep refactor in #2333 — conflicts are resolved and it's mergeable again. 🙂

Good news: the fix still applies cleanly. The -Z issue this addresses survived the refactor — it's still grep_cmd.args(["-rnHZ", "--"]) at grep_cmd.rs:352 on develop. On BSD/macOS grep, -Z means --decompress rather than the NUL separator the parser needs, so the fallback silently breaks (the "N matches in 0 files" symptom from #2310). Swapping to the portable --null long option fixes it on both GNU and BSD grep.

cargo fmt / clippy / test --all are all green locally (2197 tests), including a functional test that runs the real BSD grep this bug actually ships on.

No rush at all, but whenever you have a spare moment I'd really appreciate a look — it's a small, self-contained change. Thanks so much for maintaining rtk! @aeppling, and cc @KuSh since it touches the fallback path from #2333.

Comment thread src/cmds/system/grep_cmd.rs Outdated
@KuSh KuSh self-assigned this Jun 15, 2026
The system-grep fallback (used when ripgrep is not installed) passed
-rnHZ, relying on -Z for the NUL filename separator the match parser
requires. -Z only means --null on GNU grep; on BSD/macOS grep it is an
alias for --decompress (zgrep mode), so output is plain
file:line:content with no NUL. parse_match_line() then matches zero
filenames and every result collapses into "N matches in 0 files" with
all lines hidden behind [+N more].

Use the long option --null instead, which both GNU and BSD grep define
as "print a zero-byte after the file name".

Related to rtk-ai#2310

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vegetablechicken233 vegetablechicken233 force-pushed the fix/grep-bsd-null-fallback branch from 1c97cea to ad464a2 Compare June 16, 2026 14:32

@KuSh KuSh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

@KuSh KuSh merged commit abe7d42 into rtk-ai:develop Jun 16, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants