grep Cheat Sheet
Search text with grep: recursive, case-insensitive, whole-word, inverse and regex matching, with /etc and log file examples.
A quick reference for grep, the tool for searching text and filtering command output.
Basic search
grep "text" file.txt
grep "Listen" /etc/apache2/ports.conf
Prints every line in the file that contains the pattern.
-i case-insensitive
grep -i "error" /var/log/syslog
Matches error, Error, ERROR and so on.
-n show line numbers
grep -n "PermitRootLogin" /etc/ssh/sshd_config
Prefixes each match with its line number, so you can jump straight to it in an editor.
-v invert match
grep -v "^#" /etc/nginx/nginx.conf | grep -v "^$"
Prints lines that do not match. The example strips comments and blank lines to show only the active configuration.
-r / -R recursive
grep -r "server_name" /etc/nginx/
grep -R "server_name" /etc/nginx/
Searches every file under a directory. -r follows symlinks only when they are given on the command line; -R follows all symlinks it encounters.
-w whole word
grep -w "root" /etc/passwd
Matches root as a standalone word, not chroot or rootkit.
-c count matches
grep -c "Failed password" /var/log/auth.log
Prints the number of matching lines instead of the lines themselves.
-l / -L list filenames
grep -rl "PermitRootLogin yes" /etc/
grep -rL "Subsystem sftp" /etc/ssh/
-l prints only the names of files that contain a match; -L prints the names of files that contain no match. Useful for auditing configuration across many files.
-o only the match
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" /var/log/auth.log | sort | uniq -c | sort -rn
Prints just the matched text, not the whole line. The example extracts IP addresses from the auth log and counts them.
-E extended regex
grep -E "warning|error|critical" /var/log/syslog
grep -E "^(deb|deb-src) " /etc/apt/sources.list
Enables +, ?, |, () and {} without backslashes. egrep is the old name for the same thing.
-F fixed strings
grep -F "192.168.1.10" /etc/hosts
grep -F "[" logfile.txt
Treats the pattern literally, so regex metacharacters like . and [ match themselves. Faster, and safer when searching for text that contains punctuation.
Multiple patterns
grep -e "sshd" -e "sudo" /var/log/auth.log
grep -E "sshd|sudo" /var/log/auth.log
grep -f patterns.txt /var/log/syslog
Use repeated -e options, an alternation with -E, or -f to read one pattern per line from a file.
Combining with xargs
grep -rl "TODO" ~/project | xargs wc -l
grep -rlZ "DEBUG=true" /etc/myapp/ | xargs -0 sed -i 's/DEBUG=true/DEBUG=false/'
xargs is a separate tool, not a grep option, but the two are used together constantly: it takes the list of items grep prints on stdout and turns them into arguments for another command, rather than letting that output just scroll past. grep -rl alone only prints filenames; piped into xargs wc -l, those filenames become the files wc -l actually counts. Without xargs, a command like wc -l would just sit waiting on stdin, since grep’s output arrives as plain text on a pipe, not as command-line arguments the next program can act on.
Filenames can contain spaces, newlines, or other awkward characters that break a plain pipe into
xargs.grep -rlZ(the-Z/--nulloption) separates filenames with a NUL byte instead of a newline, andxargs -0reads that format - the only combination that’s fully safe against spaces in filenames. Plaingrep -rl | xargsworks for simple cases but splits on whitespace too, misreading a file namedmy file.txtas two separate arguments.
Combining with find -exec
find /var/log -name "*.log" -exec grep -l "OutOfMemory" {} \;
find /etc -name "*.conf" -exec grep -q "deprecated_option" {} \; -print
-exec belongs to find, not to grep, but the pairing is everywhere: find locates the files, -exec runs a command once per file found, with {} substituted for that file’s path and \; marking where the command ends. The first example finds every .log file under /var/log and runs grep -l against each one in turn, printing the name of any file that actually contains the match. The second uses grep -q (quiet - produces no output, only a pass/fail exit status) so that find’s own -print only fires for files where the grep inside -exec found something.
-exec … \;starts a brand-newgrepprocess for every single file - fine for a handful of files, slow across a huge tree.-exec … +instead batches as many files as possible onto fewer command lines (the same idea asxargs), for commands that accept multiple files at once (grepdoes):find /var/log -name "*.log" -exec grep -l "OutOfMemory" {} +runs far fewergrepprocesses over a large directory tree and finishes noticeably faster.
2>/dev/null - discarding error output
grep -r "password" /etc/ 2>/dev/null
find / -name "*.conf" 2>/dev/null | xargs grep -l "ssl_protocols" 2>/dev/null
2>/dev/null is shell redirection, not a grep option - it’s not specific to grep at all and works the same way after any command. Every command has three standard I/O streams: 0 (stdin, input), 1 (stdout, normal output), and 2 (stderr, error messages). 2>/dev/null redirects stream 2 specifically to /dev/null, a special file that silently discards anything written to it. Searching all of /etc/ as a normal user almost always hits a few directories only root can read; without this, grep -r buries the real matches under a Permission denied line for every one of them. Redirecting only stream 2 - not 1 - means the actual matches (sent to stdout) still print exactly as normal; only the permission errors are silenced.
2>/dev/nullhides every error on that stream, not just the expected “permission denied” one - a typo’d path, a missing file, or a genuine problem is suppressed exactly the same way. It’s worth running a command once without it first, to see what’s actually being thrown away, before relying on it inside a script.
Common administration examples
# Active configuration only, comments and blanks removed
grep -vE "^\s*(#|$)" /etc/ssh/sshd_config
# Which config files mention a deprecated option
grep -rn "ssl_protocols" /etc/nginx/
# Failed SSH logins, with context lines
grep -B1 -A1 "Failed password" /var/log/auth.log
# Follow a log and highlight matches live
tail -f /var/log/nginx/error.log | grep --color=always "error"