Linux grep Command: Search Text and Filter Command Output
grep is a command used to find lines that match a specified pattern in files or command output. It supports not only basic searches but also ignoring case, selecting non-matching lines, recursive directory searches, regular expressions, and displaying surrounding context.
The most basic form is grep 'pattern' filename. Since the pattern may contain characters that have special meanings in the shell, it is safer to habitually enclose it in single quotes.
What is the grep command?
grep reads input line by line and outputs lines that match the pattern. The name comes from the command expression in the Unix text editor ed, g/re/p, which means to find and print lines that match a regular expression across the entire range.
If you do not specify a file to search, it reads from standard input, so you can pipe the output of another command to search through it. The default pattern syntax of GNU grep is Basic Regular Expression (BRE), and using -E allows the use of Extended Regular Expression (ERE).
Basic Syntax and First Search
The basic syntax is as follows. Square brackets indicate optional items, so do not input them in the actual command.
grep [option] 'pattern' [file...]
To find lines containing ERROR in app.log, run as follows.
grep 'ERROR' app.log
If you specify multiple files, the filename appears along with each result.
grep 'ERROR' app.log worker.log
You can also search standard input. The following command shows lines in the kernel messages containing usb, case-insensitively.
dmesg | grep -i 'usb'
Frequently used grep options
| Option | Function | Usage Examples |
|---|---|---|
-i |
Search case-insensitively | grep -i 'error' app.log |
-v |
Select lines that do not match | grep -v '^#' config |
-n |
Show line numbers in the result | grep -n 'TODO' main.c |
-w |
Search the pattern as a whole word | grep -w 'cat' words.txt |
-x |
Select the entire line when it matches the pattern | grep -x 'enabled' status.txt |
-F |
Treat the pattern as a fixed string, not a regular expression | grep -F 'a.b' data.txt |
-E |
Use extended regular expressions | grep -E 'error|failed' app.log |
-r |
Recursively search subdirectories | grep -r 'listen' /etc/nginx |
-l |
Print only matching file names | grep -rl 'deprecated' src |
-c |
Print the count of matching lines per file | grep -c 'ERROR' app.log |
-A, -B, -C |
Print the context after, before, or around matching lines | grep -C 2 'panic' app.log |
The above options can be combined. For example, grep -in 'error' app.log will search while ignoring case and also display line numbers. Detailed behavior and GNU extension options can be found in the official GNU Grep documentation.
Distinguishing Between Strings and Regular Expressions
Search fixed strings with -F
Characters that have special meanings in regular expressions, like dots, asterisks, and brackets, can be searched literally using -F. In the following command, the dot is treated as an actual dot rather than any character.
grep -F '192.168.0.1' access.log
If the search term is a simple string, you can reduce unnecessary escapes and make your intention clear.
Use extended regular expressions with -E
Using -E allows you to use extended regular expression syntax, like |, +, ?, and parentheses, more readably. The following example finds lines that contain one of ERROR, WARN, or FATAL.
grep -E 'ERROR|WARN|FATAL' app.log
In GNU grep, the difference between basic and extended regular expressions mainly lies in the notation. If compatibility with other environments is important, check the target system's grep implementation and supported options.
Specify the start and end of a line
The caret (^) represents the beginning of a line, and the dollar sign ($) represents the end of a line. An empty line can be found with ^$, which has no characters between the start and end.
grep '^server' config.txt grep 'completed$' job.log grep '^$' notes.txt
To exclude empty lines and comment lines from a configuration file, you can apply the two conditions in sequence as follows.
grep -v '^[[:space:]]*#' app.conf | grep -v '^[[:space:]]*$'
Narrowing the search range precisely
Matching entire words and entire lines
-w selects lines where the pattern matches the entire word. For example, grep -w 'cat' will find cat but will not select cat within category.
grep -w 'root' /etc/passwd
-x selects a line only when the entire line exactly matches the pattern.
grep -x 'active' service-status.txt
Finding non-matching lines
-v reverses the selection. The following example prints only lines that do not contain DEBUG.
grep -v 'DEBUG' app.log
When combined with -i, you can exclude without regard to case.
grep -iv 'debug' app.log
Searching multiple patterns at once
Using -e multiple times allows you to specify each pattern separately. You don’t have to combine them into a single complex regular expression, making it easier to read in scripts.
grep -e 'ERROR' -e 'FATAL' app.log
If there are many patterns, you can create a file with one pattern per line and read it with -f.
grep -f patterns.txt app.log
Recursively searching entire directories
Difference between -r and -R
-r recursively searches files under the specified directory. It follows symbolic links specified directly on the command line, but skips symbolic links encountered during recursive searches.
grep -rn 'database_url' ./config
GNU grep’s -R follows symbolic links encountered during recursive searches. Since it can create circular links or a broader search scope than expected, use it when the intention is clear.
Including and Excluding File Types
--include searches only for the specified file name patterns, and --exclude skips files that match. Enclose patterns in quotes so that the shell does not expand asterisks first.
grep -rn --include='*.conf' 'timeout' /etc grep -rn --exclude='*.min.js' 'deprecated' ./src
To exclude specific directories, use --exclude-dir.
grep -rn --exclude-dir='.git' --exclude-dir='node_modules' 'api_key' .
Check only matching file names
If you need a list of files rather than matched content, use -l. Each file stops being searched after the first match is found, which also suits the purpose well.
grep -rl --include='*.html' 'jb-heading' .
Conversely, to find only files with no matching patterns, use the uppercase -L.
grep -rL --include='*.html' 'jb-heading' .
Output results in a readable format
Display line numbers and file names
-n displays the line numbers of matching lines. Even when searching a single file, to force the display of the file name, use GNU grep's -H, and to hide the file name, use -h.
grep -nH 'listen' nginx.conf
Viewing the context before and after matching lines
If it's difficult to determine the cause from error lines alone, use -A, -B, or -C to view the surrounding lines. The number indicates how many additional lines are printed.
grep -A 3 'ERROR' app.log grep -B 2 'ERROR' app.log grep -C 2 'ERROR' app.log
-A 3 adds 3 lines after, -B 2 adds 2 lines before, and -C 2 adds 2 lines before and after.
Printing only the matching part
-o prints only the part that matches the pattern, not the entire line. If it matches multiple times in one line, each match is printed separately.
grep -oE '[0-9]{3}-[0-9]{4}' contacts.txt
To highlight matches in the terminal, you can use --color=auto. When passing the output to a file or another command, use auto or never to prevent color control characters from being mixed in.
Using it in logs and command outputs
Finding Errors in Recent Logs
To search only the recent range instead of the entire log, connect it with tail.
tail -n 500 app.log | grep -iE 'error|failed|fatal'
If you want to continuously filter new logs being added to the file, you can use it as follows.
tail -F app.log | grep --line-buffered 'ERROR'
--line-buffered is a GNU extension option that flushes the output buffer as each line comes in, reducing delays in the pipe. Methods for tracing, including log rotation, can be found at How to Use the Linux tail Command.
Filtering Service and Process Results
You can search for a specific service name in the systemctl output.
systemctl list-unit-files | grep 'nginx'
If your goal is only to check the existence of a process, pgrep is often more direct than ps | grep. To search with the command name and its arguments together, you can execute it as follows.
pgrep -af 'nginx'
Counting Matching Lines
-c prints the number of matching lines, not the number of matches. Even if a line contains the pattern multiple times, that line is counted only once.
grep -c 'ERROR' app.log
To count all matching occurrences in a line, you can separate each match with -o and then count with wc -l.
grep -o 'ERROR' app.log | wc -l
Check existence without output
-q does not print results and only returns an exit status. It is suitable for checking the existence of a pattern in a shell conditional.
if grep -q 'ready' status.log; then echo 'Service ready' fi
Understanding grep exit status
grep communicates search results not only through the output string but also via the exit status. Typically, it returns 0 if there are matching lines, 1 if there are no matching lines, and 2 if it cannot read a file or there is a syntax problem.
| Exit status | Meaning | Decision in scripts |
|---|---|---|
0 |
One or more matches found | Process as if the condition is true |
1 |
No match found | Could be a normal search result |
2 |
Error occurred | Check the input file, permissions, or pattern |
Therefore, you should not conclude that the command execution itself failed just because there are no results. In automation, you need to distinguish between 1 and an actual error 2. GNU grep -q can end with status 0 if a match is found, even if there are other errors, so if detecting errors in all input files is important, separate verification is necessary.
Common problems and solutions
When the pattern starts with a hyphen
-ERROR patterns like this can be mistaken for options. Put the pattern after --, which indicates the end of options.
grep -- '-ERROR' app.log
When the 'Binary file matches' message appears
If GNU grep determines the input is a binary file, it may display the message instead of matching lines. If you are sure it is actually a text file, you can treat it as text using -a.
grep -a 'ERROR' mixed-data.log
Conversely, to exclude binary files in recursive searches, you can use -I. Do not always use -a on unknown binary data, as it may contain control characters that can mess up your terminal.
When results differ due to regex special characters
The dot matches any single character and the asterisk means repetition of the preceding expression in regular expressions. If you intended to search for a simple string, use -F, and if you intended to use a regular expression, enclose the entire pattern in single quotes to prevent shell expansion.
When recursive searches are too slow or produce too many results
Narrow down the starting directory for the search and exclude unnecessary files using --include, --exclude, and --exclude-dir. To hide only permission errors, you might be tempted to discard all standard errors, but doing so can cause you to miss important issues. Therefore, it's better to first check the search scope and file permissions.
Frequently Asked Questions
Does grep distinguish between uppercase and lowercase letters by default?
Yes. The default search is case-sensitive. To ignore case, use -i. You should also consider that the results of case handling can vary depending on the locale.
What is the difference between grep -r and grep -R?
In GNU grep, both perform recursive searches, but -R follows all symbolic links encountered during the search. For typical source code or configuration file searches, -r is easier to predict.
Is it an error if no search results are found?
Not necessarily. If there are no matches, it usually returns an exit status of 1, while errors like file access failure or invalid options are distinguished by status 2.
How do I find lines that contain at least one of multiple search terms?
grep -E 'error|warning' filename uses the alternation operator in extended regular expressions, or you can repeat -e like grep -e 'error' -e 'warning' filename. To treat the search term as a plain string rather than a regular expression, you can also use -F.
Summary
For general string searches, it's good to get familiar with grep -F; for multiple regular expression conditions, grep -E; and for searching an entire directory, grep -r. To make results easier to interpret, you can use -n and -C; if you only need the file list, use -l; and to check for existence in scripts, use -q.
Wrap patterns in single quotes, specifically limit the scope of recursive searches, and distinguish between exit status 1 and error status 2—these are key to safe usage. For exploring other basic tools together, refer to a list of frequently used Linux commands.









