How to Use grep in Linux

How to Use grep in Linux

Reading time1 min
#linux#grep#regex#command-line#sysadmin

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

OptionMeaning
-iignore case
-vinvert: lines that do not match
-nshow line numbers
-ccount matching lines
-l / -Llist files with / without a match
-wmatch whole words only
-xmatch the whole line
-oprint only the matched part
-m Nstop after N matches
-A N / -B N / -C NN lines of context after / before / around
-h / -Hhide / show file names
-shide 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