grep prints lines that match a pattern. It’s the tool for searching logs, config files and source code, and the glue in countless shell pipelines. These examples cover what you’ll use on a server day to day.
grep [options] PATTERN [FILE...]
Basic searches
grep "error" /var/log/nginx/error.log # lines containing "error"
grep -i "error" app.log # ignore case
grep -w "root" /etc/passwd # whole word only (not "chroot")
grep -x "PermitRootLogin no" /etc/ssh/sshd_config # entire line must match
grep -v "^#" /etc/ssh/sshd_config # lines NOT matching (hide comments)
grep -n "listen" /etc/nginx/sites-enabled/* # show line numbers
Show a config without comments and blank lines — a classic:
grep -Ev '^\s*(#|$)' /etc/ssh/sshd_config
Search directories recursively
grep -r "DB_PASSWORD" /var/www/ # recursive
grep -rn "TODO" src/ # with line numbers
grep -rl "base64_decode" /var/www/ # file names only
grep -rL "license" src/ # files that DON'T match
grep -r --include="*.php" "mysqli_connect" /var/www/
grep -r --exclude-dir={node_modules,.git,vendor} "apiKey" .
-l is especially useful when the next step acts on files, for example opening or fixing them.
Count and limit
grep -c "404" access.log # number of matching lines
grep -m 5 "Failed password" /var/log/auth.log # stop after 5 matches
grep -o "ERROR" app.log | wc -l # count every occurrence, even several per line
Context around matches
grep -A 5 "Traceback" app.log # 5 lines After
grep -B 2 "segfault" /var/log/syslog # 2 lines Before
grep -C 3 "OOM" /var/log/kern.log # 3 lines of Context on both sides
Perfect for stack traces and kernel messages.
Multiple patterns
grep -e "error" -e "warn" app.log # either pattern
grep -E "error|warn|crit" app.log # same, extended regex
grep -f patterns.txt app.log # patterns from a file
grep "timeout" app.log | grep -v "healthcheck" # AND NOT, via a pipe
grep "user=alice" app.log | grep "status=500" # AND, via a pipe
Regular expressions
grep uses basic regular expressions by default; -E enables extended syntax (no backslashes before |, +, ?, (), {}); -P enables Perl-compatible regex (GNU grep).
grep "^Nov 4" /var/log/syslog # lines starting with a date
grep "\.php$" files.txt # lines ending in .php
grep -E "[0-9]{1,3}(\.[0-9]{1,3}){3}" access.log # contains an IPv4 address
grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}" access.log | sort | uniq -c | sort -rn | head
grep -P "\d{3}-\d{4}" contacts.txt # \d needs -P
grep -oP 'user=\K\w+' app.log # print only what follows user=
The -oE pipeline above is a quick way to list the IPs hitting your server most.
Match a literal string containing regex characters with -F (fixed strings) — also faster:
grep -F "[error]" error.log
grep -F "$var" file # safe for variables containing dots, brackets…
Searching logs on a server
# Failed SSH logins and their source IPs
grep "Failed password" /var/log/auth.log | grep -oE "from [0-9.]+" | sort | uniq -c | sort -rn | head
# 5xx responses in an Nginx access log (status is field 9 in the combined format)
awk '$9 ~ /^5/' /var/log/nginx/access.log | tail
# Compressed rotated logs
zgrep "Out of memory" /var/log/kern.log.*.gz
# Live filtering
tail -f /var/log/nginx/access.log | grep --line-buffered " 404 "
--line-buffered makes grep emit matches immediately when it’s in the middle of a pipeline. On systemd systems, the journal has its own filter: journalctl -u nginx | grep -i error, or journalctl -g "pattern".
Colour and output tweaks
grep --color=auto "error" app.log # usually aliased by default
grep -h "pattern" *.log # no file name prefix
grep -H "pattern" single.log # force file name prefix
grep -s "pattern" /etc/* # suppress "permission denied" errors
grep -q "pattern" file && echo found # quiet: only the exit status
Use grep in scripts
grep’s exit status: 0 = matched, 1 = no match, 2 = error.
if grep -q "^PermitRootLogin yes" /etc/ssh/sshd_config; then
echo "Root login is enabled!"
fi
if ! grep -qxF "vm.swappiness=10" /etc/sysctl.conf; then
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf
fi
The second example appends a line only if it isn’t there yet — idempotent config edits.
Faster alternatives
For big codebases, ripgrep (rg) is much faster than grep -r and respects .gitignore automatically: sudo apt install ripgrep, then rg "pattern". For logs and config files on a server, plain grep is always available and more than fast enough.
Quick reference
| Option | Meaning |
|---|---|
-i |
Ignore case |
-v |
Invert match |
-w / -x |
Whole word / whole line |
-n |
Line numbers |
-c |
Count matching lines |
-r |
Recursive |
-l / -L |
Files with / without matches |
-o |
Only the matching part |
-A/-B/-C n |
Context lines |
-E / -F / -P |
Extended / fixed / Perl regex |
-q |
Quiet, exit status only |
Pair grep with find to locate files, and with sed and awk to transform what it finds. All three come preinstalled on every Linux VPS.