{
"schema_version": "1.0",
"command": "grep",
"variant": "gnu",
"match": {
"probe": {
"args": ["--version"],
"expect": "success",
"output_contains": "GNU grep"
}
},
"mode": "authoritative",
"confidence": "verified",
"provenance": {
"platform": "linux",
"tool_version": "3.11",
"package": "grep",
"source": "help",
"checked_on": "2026-07-27",
"command": "docker run --rm debian@sha256:328d16499860ae6cb9b345e2e4cebca08c2a36e4f7278482c7bd1f39d71e5bfd grep --help",
"environment": "debian@sha256:328d16499860ae6cb9b345e2e4cebca08c2a36e4f7278482c7bd1f39d71e5bfd",
"notes": "Debian slim images ship no man pages, so the flag list was read from GNU --help, which is complete by GNU convention -- and here it very nearly is, with three exceptions found by sweeping every ASCII letter against the binary. -u, -y and -X are accepted and appear NOWHERE in --help, the same failure mode as BSD 'touch -f'. Each was then run to establish what it does rather than guessed: 'grep -y HELLO' matches the same lines as 'grep -i HELLO', so -y is the obsolete synonym for -i; 'grep -u -b' prints 'warning: --unix-byte-offsets (-u) is obsolete' and then the same offset as -b alone, and the long form --unix-byte-offsets is accepted too; -X takes a matcher name, since 'grep -X hello' fails with 'invalid matcher hello' while 'grep -X egrep' works, and there is no --matcher long form (it is rejected). The leading digits are real standalone options: 'grep -1 pattern file' prints one line of context. They are a family of numeric context sizes rather than named options, so the overlay format cannot carry them. -A, -B and -C all REQUIRE a value here; unlike BSD grep there is no optional-argument form, and bare --context fails with 'invalid context length argument'. mode is authoritative because the short set is closed by the sweep at 'abcdefhilmnoqrsuvwxyzABCDEFGHILPRTUVXZ' plus digits, and every long option in --help was run and accepted. Five long options that BSD grep has were tested and REJECTED here, so they are absent: --mmap, --bz2decompress, --lzma, --xz, --decompress and --include-dir. conflicts_with was read off the running binary: all 12 ordered pairs among -E, -F, -G and -P are diagnosed with 'conflicting matchers specified', and nothing else in the option set is diagnosed at all. Note that BSD grep accepts exactly those pairings, which is why the two variants carry different conflicts -- the descriptions and the conflicts were derived from each variant's own evidence, never copied across. -X carries no conflicts_with because its exclusivity is value-dependent, the same shape as sort's --sort=WORD: 'grep -X egrep -E' is accepted because both name the same matcher, while 'grep -X egrep -F' is diagnosed. Candidates tested and rejected: -c with -l, -L and -o; -l with -L; -n with -c, -l, -L and -q, which BSD grep's man page does describe as ignored and which this --help does not, so the conflict is declared on the BSD side only; -i with --no-ignore-case and --group-separator with --no-group-separator, both of which are opposites that the binary resolves by last-one-wins rather than by diagnosing, the same treatment diff@bsd gives --ignore-file-name-case. annotations: grep opens every operand read-only and has no option that names an output file, so readonly is true; requires_approval is stated false to override the scanner's flag heuristic, which would otherwise escalate on -r/-R/--recursive even though recursion here only widens what is read. conflicts_with was extended in a later pass, after the field's definition widened to cover override groups as well as diagnosed errors: apexe's input is a JSON object, which carries no ordering, so for a last-one-wins group which flag actually wins is decided by the order the caller happened to write the keys in. Every pair added then was run in BOTH orders against the reference build recorded above, and only pairs whose outcome depends on that order, or where one side is silently discarded, were added. value_optional was added on 2026-08-24, read off the reference build recorded above. GNU --help spells an optional option argument as `--opt[=VAL]` and a required one as `--opt=VAL`; every flag marked here shows the bracketed form and was then run in BOTH spellings, because the notation alone is not evidence. `grep --color never hello g.txt` is \"grep: hello: No such file or directory\" (exit 2) -- the value consumed the pattern position -- while `grep --color=never hello g.txt` succeeds; --colour is the same option under its other spelling and was checked separately. --context was tested and deliberately NOT marked: GNU grep spells it '-C, --context=NUM' with a required value and `grep --context 1 hello g.txt` succeeds here. That is the OPPOSITE of BSD grep, where --context is optional-valued; the two variants must not be copied from one another on this point."
},
"description": "Search for PATTERNS in each FILE. PATTERNS can contain multiple patterns separated by newlines. When FILE is '-', read standard input. With no FILE, read '.' if recursive, '-' otherwise. With fewer than two FILEs, assume -h. Exit status is 0 if any line is selected, 1 otherwise; if any error occurs and -q is not given, the exit status is 2.",
"flags": [
{ "short": "-E", "long": "--extended-regexp", "type": "boolean", "conflicts_with": ["-F", "-G", "-P"], "description": "PATTERNS are extended regular expressions." },
{ "short": "-F", "long": "--fixed-strings", "type": "boolean", "conflicts_with": ["-E", "-G", "-P"], "description": "PATTERNS are strings." },
{ "short": "-G", "long": "--basic-regexp", "type": "boolean", "conflicts_with": ["-E", "-F", "-P"], "description": "PATTERNS are basic regular expressions." },
{ "short": "-P", "long": "--perl-regexp", "type": "boolean", "conflicts_with": ["-E", "-F", "-G"], "description": "PATTERNS are Perl regular expressions." },
{ "short": "-X", "type": "enum", "value_name": "MATCHER", "enum_values": ["grep", "egrep", "fgrep", "awk", "gawk", "posixawk", "perl"], "description": "Select the matcher by name, the general form of which -E, -F, -G and -P are shorthands. Accepted by the binary but shown nowhere in --help, and there is no long form. It carries no conflicts_with because the clash is value-dependent: 'grep -X egrep -E' is accepted while 'grep -X egrep -F' is diagnosed as conflicting matchers. The values listed here are the matcher names GNU grep 3.11 accepts; an unknown one fails with 'invalid matcher'." },
{ "short": "-e", "long": "--regexp", "type": "string", "value_name": "PATTERNS", "repeatable": true, "description": "Use PATTERNS for matching." },
{ "short": "-f", "long": "--file", "type": "path", "value_name": "FILE", "repeatable": true, "description": "Take PATTERNS from FILE." },
{ "short": "-i", "long": "--ignore-case", "type": "boolean", "description": "Ignore case distinctions in patterns and data." },
{ "long": "--no-ignore-case", "type": "boolean", "description": "Do not ignore case distinctions (default)." },
{ "short": "-y", "type": "boolean", "description": "Obsolete synonym for -i. Accepted by the binary but shown nowhere in --help; verified by matching the same lines as -i on mixed-case input." },
{ "short": "-w", "long": "--word-regexp", "type": "boolean", "description": "Match only whole words." },
{ "short": "-x", "long": "--line-regexp", "type": "boolean", "description": "Match only whole lines." },
{ "short": "-z", "long": "--null-data", "type": "boolean", "description": "A data line ends in 0 byte, not newline." },
{ "short": "-s", "long": "--no-messages", "type": "boolean", "description": "Suppress error messages." },
{ "short": "-v", "long": "--invert-match", "type": "boolean", "description": "Select non-matching lines." },
{ "short": "-V", "long": "--version", "type": "boolean", "description": "Display version information and exit. This build answers with 'grep (GNU grep) 3.11'." },
{ "long": "--help", "type": "boolean", "description": "Display this help text and exit." },
{ "short": "-m", "long": "--max-count", "type": "integer", "value_name": "NUM", "description": "Stop after NUM selected lines." },
{ "short": "-b", "long": "--byte-offset", "type": "boolean", "description": "Print the byte offset with output lines." },
{ "short": "-u", "long": "--unix-byte-offsets", "type": "boolean", "description": "Obsolete no-op retained for MS-DOS compatibility; the binary prints 'warning: --unix-byte-offsets (-u) is obsolete' and the byte offsets are unchanged. Accepted in both forms but shown nowhere in --help." },
{ "short": "-n", "long": "--line-number", "type": "boolean", "conflicts_with": ["-c", "-l", "-L", "-q"], "description": "Print line number with output lines." },
{ "long": "--line-buffered", "type": "boolean", "description": "Flush output on every line." },
{ "short": "-H", "long": "--with-filename", "type": "boolean", "conflicts_with": ["-h"], "description": "Print file name with output lines." },
{ "short": "-h", "long": "--no-filename", "type": "boolean", "conflicts_with": ["-H"], "description": "Suppress the file name prefix on output." },
{ "long": "--label", "type": "string", "value_name": "LABEL", "description": "Use LABEL as the standard input file name prefix." },
{ "short": "-o", "long": "--only-matching", "type": "boolean", "conflicts_with": ["-c", "-l", "-L", "-q"], "description": "Show only nonempty parts of lines that match." },
{ "short": "-q", "long": "--quiet", "type": "boolean", "conflicts_with": ["-c", "-l", "-L", "-o", "-n"], "description": "Suppress all normal output." },
{ "long": "--silent", "type": "boolean", "description": "The same as -q and --quiet: suppress all normal output." },
{ "long": "--binary-files", "type": "enum", "value_name": "TYPE", "enum_values": ["binary", "text", "without-match"], "description": "Assume that binary files are TYPE." },
{ "short": "-a", "long": "--text", "type": "boolean", "description": "Equivalent to --binary-files=text." },
{ "short": "-I", "type": "boolean", "description": "Equivalent to --binary-files=without-match." },
{ "short": "-d", "long": "--directories", "type": "enum", "value_name": "ACTION", "enum_values": ["read", "recurse", "skip"], "description": "How to handle directories." },
{ "short": "-D", "long": "--devices", "type": "enum", "value_name": "ACTION", "enum_values": ["read", "skip"], "description": "How to handle devices, FIFOs and sockets." },
{ "short": "-r", "long": "--recursive", "type": "boolean", "description": "Like --directories=recurse." },
{ "short": "-R", "long": "--dereference-recursive", "type": "boolean", "description": "Like -r, but follow all symlinks." },
{ "long": "--include", "type": "string", "value_name": "GLOB", "repeatable": true, "description": "Search only files that match GLOB (a file pattern)." },
{ "long": "--exclude", "type": "string", "value_name": "GLOB", "repeatable": true, "description": "Skip files that match GLOB." },
{ "long": "--exclude-from", "type": "path", "value_name": "FILE", "repeatable": true, "description": "Skip files that match any file pattern from FILE." },
{ "long": "--exclude-dir", "type": "string", "value_name": "GLOB", "repeatable": true, "description": "Skip directories that match GLOB." },
{ "short": "-L", "long": "--files-without-match", "type": "boolean", "conflicts_with": ["-c", "-l", "-o", "-q", "-n"], "description": "Print only names of FILEs with no selected lines." },
{ "short": "-l", "long": "--files-with-matches", "type": "boolean", "conflicts_with": ["-c", "-L", "-o", "-q", "-n"], "description": "Print only names of FILEs with selected lines." },
{ "short": "-c", "long": "--count", "type": "boolean", "conflicts_with": ["-l", "-L", "-o", "-q", "-n"], "description": "Print only a count of selected lines per FILE." },
{ "short": "-T", "long": "--initial-tab", "type": "boolean", "description": "Make tabs line up (if needed)." },
{ "short": "-Z", "long": "--null", "type": "boolean", "description": "Print 0 byte after FILE name." },
{ "short": "-B", "long": "--before-context", "type": "integer", "value_name": "NUM", "description": "Print NUM lines of leading context. The value is required; unlike BSD grep there is no optional-argument form." },
{ "short": "-A", "long": "--after-context", "type": "integer", "value_name": "NUM", "description": "Print NUM lines of trailing context. The value is required; unlike BSD grep there is no optional-argument form." },
{ "short": "-C", "long": "--context", "type": "integer", "value_name": "NUM", "description": "Print NUM lines of output context. The value is required; bare --context fails with 'invalid context length argument', which is where this differs from BSD grep." },
{ "long": "--group-separator", "type": "string", "value_name": "SEP", "description": "Print SEP on line between matches with context." },
{ "long": "--no-group-separator", "type": "boolean", "description": "Do not print separator for matches with context." },
{ "long": "--color", "type": "enum", "value_name": "WHEN", "value_optional": true, "enum_values": ["always", "never", "auto"], "description": "Use markers to highlight the matching strings. The value is optional and must be attached, as in --color=always." },
{ "long": "--colour", "type": "enum", "value_name": "WHEN", "value_optional": true, "enum_values": ["always", "never", "auto"], "description": "Use markers to highlight the matching strings. GNU help lists --color[=WHEN] and --colour[=WHEN] as one entry, so this is the alternate spelling rather than a separate option. The value is optional and must be attached, as in --colour=always." },
{ "short": "-U", "long": "--binary", "type": "boolean", "description": "Do not strip CR characters at EOL (MSDOS/Windows)." }
],
"positional_args": [
{ "name": "patterns", "type": "string", "required": false, "description": "The patterns to search for, separated by newlines. Not given when -e or -f supplies the patterns instead, in which case the first operand is read as a file name." },
{ "name": "file", "type": "path", "variadic": true, "required": false, "description": "Files to search. When FILE is '-', read standard input. With no FILE, read '.' if recursive and '-' otherwise." }
],
"annotations": {
"readonly": true,
"destructive": false,
"idempotent": true,
"requires_approval": false
}
}