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.

More in This Category
Linux id Command: Show User and Group IDs

Linux id Command: Show User and Group IDs

Learn how to show User and Group IDs with the Linux id command, including practical examples, key options, and important precautions.

Linux screen Command: Create and Manage Detachable Terminal Sessions

Linux screen Command: Create and Manage Detachable Terminal Sessions

Learn how to use the Linux screen command to keep terminal sessions running and reconnect to them later, with essential options, practical examples, output interpretation, and common troubleshooting tips.

Linux time Command: Measure Command Execution Time

Linux time Command: Measure Command Execution Time

Learn how to use the Linux time command to measure elapsed, user CPU, and system CPU time for a command, with essential options, practical examples, output interpretation, and common troubleshooting tips.

Linux xargs Command: Pass Standard Input as Command Arguments

Linux xargs Command: Pass Standard Input as Command Arguments

Learn how to pass Standard Input as Command Arguments with the Linux xargs command, including practical examples, key options, and important precautions.

Linux ssh-keygen Command: Generate SSH Keys and Inspect Fingerprints

Linux ssh-keygen Command: Generate SSH Keys and Inspect Fingerprints

Learn how to generate SSH Keys and Inspect Fingerprints with the Linux ssh-keygen command, including practical examples, key options, and important precautions.

Linux apt Command: Manage Packages on Debian and Ubuntu

Linux apt Command: Manage Packages on Debian and Ubuntu

Learn how to manage Packages on Debian and Ubuntu with the Linux apt command, including practical examples, key options, and important precautions.

Linux whoami Command: Show the Effective User Name

Linux whoami Command: Show the Effective User Name

Learn how to show the Effective User Name with the Linux whoami command, including practical examples, key options, and important precautions.

Linux tar Command: Create and Extract Archives

Linux tar Command: Create and Extract Archives

Learn how to create and Extract Archives with the Linux tar command, including practical examples, key options, and important precautions.

Linux sftp Command: Transfer Files Securely over SSH

Linux sftp Command: Transfer Files Securely over SSH

Learn how to use the Linux sftp command to transfer and manage files securely through an SSH connection, with essential options, practical examples, output interpretation, and common troubleshooting tips.

Linux uniq Command: Remove or Count Adjacent Duplicate Lines

Linux uniq Command: Remove or Count Adjacent Duplicate Lines

Learn how to remove or count adjacent duplicate lines with the Linux uniq command, including practical examples, key options, and important precautions.