Basic syntax
grep [OPTIONS] PATTERN [FILE...]
By default grep uses BRE (Basic Regular Expressions). That means +, ?,
(, ), {, } in the pattern are treated as literals unless escaped. To get
the usual regex behavior, use -E (or egrep, which is the same thing):
grep 'foo\+' file # BRE: + must be escaped
grep -E 'foo+' file # ERE: no escape needed
Key flags
| flag | meaning |
|---|---|
-i | case-insensitive |
-v | invert (lines with NO match) |
-n | prefix each line with its line number |
-c | print only the count of matching lines |
-l | print only the names of files that have a match |
-L | opposite: files with NO match |
-o | print only the matched part of each line |
-w | match whole words only |
-x | match whole lines only |
-r / -R | recursive tree walk (R follows symlinks) |
-A N / -B N / -C N | context: After / Before / Around |
--include='*.py' | restrict files matched by -r |
--exclude-dir=.git | skip a directory |
Regex modes
grep -F 'a.b.c' file # -F: fixed string, dots are literal dots
grep 'a.b' file # BRE: . = any character
grep -E '^[0-9]+$' file # ERE: [], +, {}, () work without \grep -P '\d{3}-\d{4}' file # PCRE: \d, \w, look-ahead, named groupsPCRE gives the most expressive patterns (lookahead, non-greedy, Perl-style
character classes), but requires grep built with --enable-perl-regexp
(Ubuntu ships with it enabled by default).
Recursive search
grep -rn 'TODO' . # all TODOs in the project, with line numbers
grep -rni --include='*.py' 'fixme' src/ # only in .py files
grep -rln 'API_KEY' /etc # names of files that mention it
grep -rn 'password' . --exclude-dir=.git --exclude-dir=node_modules
Without -r, grep will complain: Is a directory.
Context
grep -n -B 2 -A 2 'ERROR' app.log # 2 lines before and after
grep -C 3 'panic' /var/log/syslog # 3 lines on each side
Anchors and groups
grep '^Host' ~/.ssh/config # start of line
grep '\.log$' filelist # end of line
grep -E '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' file # IPv4 (see [[ipv4-addressing]])grep -Eo '[a-z0-9.]+@[a-z0-9.]+' # extract email addresses
Combining with other tools
The most common pattern is a pipe from cmd-find:
find . -type f -name '*.conf' -print0 | xargs -0 grep -l 'listen 80'
▸find all config files that mention "listen 80"
Or from the output of another command:
ps aux | grep -v grep | grep nginx # classic "exclude the grep line itself"
ss -tn | grep ESTAB # ESTABLISHED sessions
dmesg | grep -i 'oom' # see [[oom-killer]]
The | grep -v grep idiom is needed because ps aux | grep nginx would also
match the grep nginx process in the ps output.
Exit codes (important for scripts)
0- at least one match was found1- no matches2- error (missing file, bad regex)
In bash:
if grep -q ERROR app.log; then
echo "errors present"
fi
-q (quiet) produces no output; it only sets the exit code.
ripgrep as a modern alternative
rg (ripgrep) is orders of magnitude faster than grep -r. It respects
.gitignore automatically, runs searches in parallel, and uses SIMD. On large
repositories the difference is dramatic. That said, grep is POSIX and available
everywhere, so knowing it is essential.