How to Use grep in Linux
grep prints the lines that match a pattern. It reads files or the output of another command, which makes it the default tool for searching logs, configs and source code from the terminal.
Basic usage
grep PATTERN [FILE...]
grep 'error' app.log
grep 'listen' /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf
ps aux | grep '[n]ginx'
Put the pattern in single quotes so the shell leaves $, * and spaces alone.
The [n]ginx trick in the last line stops grep from matching its own process. pgrep -a nginx does the same job more cleanly.
grep exits with status 0 when something matched, 1 when nothing did and 2 on an error. In scripts, -q suppresses output so only the status counts:
if grep -q '^ID=ubuntu' /etc/os-release; then
echo "Ubuntu"
fi
Options worth remembering
| Option | Meaning |
|---|---|
-i | ignore case |
-v | invert: lines that do not match |
-n | show line numbers |
-c | count matching lines |
-l / -L | list files with / without a match |
-w | match whole words only |
-x | match the whole line |
-o | print only the matched part |
-m N | stop after N matches |
-A N / -B N / -C N | N lines of context after / before / around |
-h / -H | hide / show file names |
-s | hide errors about missing or unreadable files |
grep -in 'timeout' config.yml
grep -c 'POST' access.log
grep -w 'id' schema.sql # matches "id", not "user_id"
grep -vE '^\s*(#|$)' /etc/ssh/sshd_config # active settings only
The last example drops comment lines and empty lines, a quick way to see what a long config actually sets.
-c counts lines, not occurrences. A line with three matches counts once. To count every occurrence:
grep -o 'ERROR' app.log | wc -l
Context around a match
grep -A 5 'Traceback' app.log # match plus 5 lines after
grep -B 2 'Killed process' kern.log # 2 lines before
grep -C 3 'panic' boot.log # 3 lines on both sides
Recursive search in directories
grep -rn 'DATABASE_URL' .
grep -rn --include='*.py' 'import requests' src/
grep -rn --exclude-dir={.git,node_modules,vendor} 'TODO' .
grep -rl 'old.example.com' /etc/nginx/
grep -rI 'password' /srv/my-app # skip binary files
-r searches a directory tree. It follows symlinks given on the command line but not ones it finds inside the tree. -R follows all of them. --include, --exclude and --exclude-dir take glob patterns, and the {a,b,c} form is bash brace expansion into several options. Without -I, matches in binary files only print binary file matches.
Regular expressions
By default grep uses basic regular expressions, where +, ?, |, () and {} need a backslash to be special. With -E (extended) they work without one, which is easier to read:
grep '^ERROR' app.log # line starts with ERROR
grep 'done$' build.log # line ends with done
grep -E 'ERROR|WARN' app.log # either word
grep -E '^(GET|POST) /api/' requests.log
grep -E 'v[0-9]+\.[0-9]+\.[0-9]+' CHANGELOG.md # version numbers
grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log
. matches any character, so escape it in IP addresses and domain names: grep 'example\.com'.
Two other modes:
grep -F '[ERROR] (db)' app.log # fixed string, no regex at all
grep -P '\d{4}-\d{2}-\d{2}' app.log # Perl-compatible regex
grep -oP 'user=\K\w+' auth.log # print only the value after user=
-F is the right choice for literal text full of brackets and dots. -P adds \d, lookarounds and \K, which discards everything matched before it. It is a GNU grep feature and is not available in every grep build.
egrep and fgrep are obsolete. Since GNU grep 3.8 they print a warning; use grep -E and grep -F.
To search for several patterns, repeat -e or read them from a file:
grep -e 'timeout' -e 'refused' app.log
grep -f patterns.txt app.log
grep in pipelines
journalctl -u nginx --since today | grep -i error
sudo dmesg | grep -i usb
kubectl get pods -A | grep -v Running
history | grep ssh
tail -f app.log | grep --line-buffered ERROR
zgrep 'Failed password' /var/log/auth.log.*.gz
--line-buffered makes grep print each match immediately instead of in blocks, which matters when its output goes into another pipe while following a log. zgrep searches gzip-compressed files directly, handy for rotated logs.
Most distributions alias grep to grep --color=auto, which highlights matches in the terminal. To keep colors when paging, use grep --color=always ... | less -R.
Faster tools for code
In a Git repository, git grep searches only tracked files and is very fast. For large trees, rg (ripgrep) is recursive by default, skips .gitignore entries and binary files, and uses all CPU cores. Install it with sudo apt install ripgrep or sudo dnf install ripgrep (EPEL on RHEL). To search for files by name rather than content, see How to find files in Linux.
Quick reference
grep 'text' file # basic search
grep -i 'text' file # ignore case
grep -v 'text' file # lines without text
grep -n 'text' file # with line numbers
grep -c 'text' file # count matching lines
grep -w 'word' file # whole word
grep -C 3 'text' file # with context
grep -rn 'text' dir/ # recursive
grep -rl 'text' dir/ # only file names
grep -rn --include='*.yml' 'text' . # only some files
grep -E 'one|two' file # extended regex
grep -F 'a.b[c]' file # literal string
grep -o 'pattern' file | wc -l # count occurrences
command | grep 'text' # filter output
