grep Command Explained
grep searches text for patterns, and it is genuinely one of the most-used tools across Linux system administration, software development, and general troubleshooting. Its name comes from an old text editor command (g/re/p, global regular expression print), and the behavior it describes is exactly what it still does today.
Basic usage
grep "ERROR" app.log
# prints every line in app.log containing the text "ERROR"
grep "ERROR" app.log server.log
# searches both files, prefixing each match with its filename
Given a pattern and a file, grep prints every line containing a match. Given multiple files, it also prefixes each matching line with the filename it came from, so you can tell which file each result belongs to.
Recursive search across a directory tree
grep -r "TODO" .
# search every file under the current directory and its
# subdirectories for the text "TODO"
grep -rn "ERROR" /var/log
# same, but -n also adds the line number of each match
-r (recursive) is one of the most commonly used grep flags, turning a single-file search tool into a full codebase or log-directory search. Combined with -n (line numbers), the output tells you exactly which file and which line number every match came from.
Case-insensitive search
grep -i "error" app.log
# matches "error", "Error", "ERROR", and any other case variant
By default, grep is case-sensitive. -i disables that, matching regardless of capitalization, which is useful since log messages, error text, and configuration keys are not always capitalized consistently.
Inverting the match: showing non-matching lines
grep -v "DEBUG" app.log
# prints every line that does NOT contain "DEBUG"
-v flips the logic entirely: instead of showing lines that match, it shows every line that does not match. This is genuinely useful for filtering out expected, high-volume noise (routine debug logging, for instance) to focus on everything else that remains.
Showing context around a match
grep -A 3 "ERROR" app.log
# show the matching line plus the 3 lines AFTER it
grep -B 3 "ERROR" app.log
# show the matching line plus the 3 lines BEFORE it
grep -C 3 "ERROR" app.log
# show 3 lines of context on BOTH sides of the match
-A, -B, and -C (after, before, context) are extremely useful when a single matching line alone does not give enough information to understand what actually happened, such as an error message whose surrounding lines provide the stack trace or the request that triggered it.
Counting matches instead of printing them
grep -c "ERROR" app.log
# just the number of matching lines, not the lines themselves
grep -l "ERROR" *.log
# just the FILENAMES that contain at least one match,
# not the matching lines themselves
-c gives a count. -l (lowercase L) lists only the names of files containing at least one match, which is useful when searching many files and you only need to know which ones are relevant before digging deeper into any specific one.
Regular expressions: basic vs extended
grep "err[o0]r" app.log
# basic regex: matches "error" or "err0r"
grep -E "error|warning|critical" app.log
# extended regex (-E): the pipe | means OR, without
# needing to escape it as \|
grep "error\|warning" app.log
# the same OR logic is possible in basic regex too,
# but requires escaping the pipe character
grep supports regular expressions by default, using what is called Basic Regular Expression (BRE) syntax, where some special characters like +, ?, and | need to be escaped with a backslash to carry their special meaning. -E switches to Extended Regular Expression (ERE) syntax, where those characters work directly without escaping, which most people find considerably more readable for anything beyond a simple literal string search.
Whole-word matching
grep "cat" file.txt
# matches "cat", but ALSO matches "category", "concatenate", etc.
grep -w "cat" file.txt
# matches only the standalone word "cat", not as a substring
# of a longer word
-w restricts matches to whole words only, which prevents a short search term from also matching as a substring inside longer, unrelated words.
Searching piped output
ps aux | grep nginx
# find nginx-related processes among all running processes
journalctl | grep -i "failed"
# search the systemd journal for anything mentioning "failed"
grep reads from standard input just as readily as it reads from files, which is why it appears constantly at the end of a pipeline, filtering down the output of another command to just the lines that matter.
Frequently Asked Questions
What does the grep command do?
grep searches text, whether from a file, multiple files, or piped input, for lines matching a given pattern, and prints every matching line. The name is a historical acronym from an old text editor command, global regular expression print, and it remains one of the most frequently used tools in Linux for searching logs, source code, and configuration files.
How do I search for a pattern across every file in a directory tree?
Use the -r (recursive) flag, such as grep -rn “ERROR” /var/log, which searches every file under /var/log and its subdirectories for lines containing “ERROR”, printing the matching line along with its line number thanks to -n.
What does grep -i do?
-i makes the search case-insensitive, so grep -i “error” file.log matches “error”, “Error”, “ERROR”, and any other capitalization variant. Without -i, grep matches the exact case specified in the pattern, which can cause a search to miss relevant lines if the actual capitalization in the file differs from what was typed.
How do I search for lines that do NOT match a pattern?
Use the -v flag (invert match), such as grep -v “DEBUG” app.log, which prints every line except those containing “DEBUG”. This is useful for filtering out noisy, expected log lines to focus on everything else.
Does grep support regular expressions, and how powerful are they?
Yes, grep supports regular expressions by default (basic regular expressions), and -E enables extended regular expressions, which support additional syntax like + and | without needing to escape them with a backslash. A pattern like grep -E “error|warning|critical” file.log matches any line containing any one of those three words, using extended regex alternation.
How do I see the file name and line number for every match when searching multiple files?
grep automatically prefixes each match with the filename when searching multiple files or using -r, and the -n flag adds the line number as well, producing output like config.yaml:42:debug: true, which tells you both exactly which file and exactly which line the match came from.