Syntax
Options can come before or after the pattern. With more than one file, grep prefixes every output line with the file name.
grep [options] PATTERN [file ...]| Exit code | Meaning |
|---|---|
| At least one line matched | |
| Nothing matched | |
| An error: missing file, unreadable directory, bad pattern |
The Basics
| Command | What it does |
|---|---|
| Print lines containing error | |
| Case insensitive (matches Error, ERROR) | |
| Show line numbers | |
| Invert: lines that do NOT match | |
| Count matching lines instead of printing them | |
| Match whole words only (skips passport) | |
| Match whole lines only | |
| Print only the matched text, not the whole line | |
| Stop after the first match | |
| List files that match, not the lines | |
| List files that do NOT match | |
| Fixed string: treat the pattern literally, no regex | |
| Silent; exit code only (for in scripts) | |
| Hide the file name prefix | |
| Force the file name prefix on a single file | |
| Keep the highlighting through a pager |
Recursive Search
matches each directory's basename, not its path, so one name skips it everywhere in the tree.
| Command | What it does |
|---|---|
| Search every file under the current directory | |
| Recursive with file name and line number | |
| Recursive, case insensitive | |
| Just the file names (feeds nicely into xargs) | |
| Only search .py files | |
| Skip minified files | |
| Skip a directory | |
| Skip several directories | |
| Like -r, but also follows symlinks |
Multiple Patterns
| Command | What it does |
|---|---|
| OR: match either pattern | |
| OR with extended regex | |
| Read patterns from a file, one per line | |
| AND: lines containing both | |
| Match one, exclude another | |
| Exclude several patterns | |
| Count lines matching either | |
| Lines that appear in both files | |
| Lines in new.txt that are not in old.txt |
Counting Matches
| Command | What it does |
|---|---|
| Count matching lines | |
| Count occurrences, including several per line | |
| Count the lines that do not match | |
| Per-file counts across a tree | |
| Same, hiding files with no matches | |
| Rank the distinct matches by frequency |
counts lines, not matches: a line with three hits still counts as one. Use piped into when you need the real total.
Regex
grep's default dialect is basic regex (BRE). Add for extended regex (ERE), where , , , and work without backslashes. works on every grep, including macOS.
| Pattern | Matches |
|---|---|
| Any single character | |
| Zero or more of the previous item | |
| error at the start of the line | |
| error at the end of the line | |
| Empty lines | |
| Any one of these characters | |
| Any character that is not a digit | |
| Character ranges | |
| , , | POSIX classes (portable stand-ins for \d, \s) |
| A literal dot (escape to match them literally) | |
| Word boundaries (same idea as -w) |
With :
| Pattern | Matches |
|---|---|
| One or more: gogle, google, gooogle | |
| Optional: color and colour | |
| Alternation | |
| Grouping: ab, abab | |
| Repetition counts (start of an IP address) |
unlocks Perl regex (, , lookaheads) but only exists in GNU grep. The grep on macOS and BSD does not have it: use with POSIX classes, or and call .
Context Lines
| Command | What it does |
|---|---|
| Show 3 lines After each match | |
| Show 3 lines Before each match | |
| 3 lines of Context on both sides | |
| Context plus line numbers |
Groups of matches are separated by a line. Nearby matches merge into one block.
Pipes and xargs
grep is at its best at the end of a pipe. See the awk cheat sheet for what usually comes after it.
| Command | What it does |
|---|---|
| Find a running process (see the ps cheat sheet) | |
| Same, without grep matching its own process line | |
| Just the PIDs of the java processes | |
| The same list without the grep trick | |
| Kill by full command line instead of | |
| Search your shell history | |
| Kernel messages about USB | |
| Which proxy variables are set | |
| Which log files contain error | |
| Find files, then edit them (see the sed cheat sheet) | |
| Safe with spaces in file names (see the find cheat sheet) |
Searching Logs
| Command | What it does |
|---|---|
| The everyday log sweep | |
| Each error plus the lines under it | |
| Live tail, filtered (without output stalls) | |
| Filter a unit's journal (see the journalctl cheat sheet) | |
| Docker sends some logs to stderr, so redirect first | |
| Same idea for a Kubernetes deployment | |
| One morning's lines, by timestamp prefix |
For a real range between two timestamps, is the better tool. See the awk cheat sheet.
Compressed Files
| Command | What it does |
|---|---|
| Search a gzipped file without unpacking it | |
| Rotated logs, plain and gzipped in one pass | |
| Count inside a compressed file | |
| The same thing by hand | |
| bzip2 archives | |
| xz archives | |
| Stream a tarball's contents through grep (see tar) | |
| Search the text inside a zip |
takes the same flags as grep, so , and all carry over.
grep on Windows
Windows has no grep built in, but both shells have an equivalent.
| Command | What it does |
|---|---|
| cmd: basic search | |
| cmd: case insensitive, recursive (roughly ) | |
| cmd: invert, like | |
| cmd: regex, in findstr's own limited dialect | |
| PowerShell: the real grep equivalent, full regex | |
| PowerShell: grep is case sensitive by default, Select-String is not | |
| PowerShell: recursive search | |
| PowerShell: sls is the short alias |
Git Bash and WSL both ship genuine GNU grep, so works there unchanged.
Alternatives to grep
grep is on every machine you will ever log into. These are faster in a large source tree, but you have to install them first.
| Command | What it does |
|---|---|
| ripgrep: recursive by default and skips anything in .gitignore | |
| Case insensitive, limited to Python files | |
| Turn the .gitignore and hidden-file filtering back off | |
| Search only tracked files, from anywhere in the repo (see git) | |
| Search every commit in the history | |
| The Silver Searcher, the same idea as ripgrep | |
| GNU grep on macOS, after |
Gotchas
- is GNU-only. On macOS, fails with "invalid option"; use or install GNU grep (, then ).
- Always quote the pattern. Unquoted, the shell expands and against your files before grep ever sees them.
- grep exits 1 when nothing matches. In a script with that kills the script; append when no-match is fine.
- In basic regex, and are literal characters. matches the literal text go+gle; you want .
- "Binary file matches" means the file has non-text bytes. forces it to be treated as text, and skips binary files entirely during a recursive search.
- with no file name is not hung, it is waiting on standard input. Ctrl+C out, then give it a file or a pipe.
- A file written on Windows ends each line with a carriage return, so finds nothing. Run on it first.
- works, but is one process instead of two and lets grep print the file name.
- grep finds lines, sed edits them, and awk works on columns. The three cover most text jobs between them.