Skip to main content
Blog|
Learning center

grep Command: A Practical Guide with Examples

|
Mar 20, 2026|10 min read
LEARNING CENTERgrep Command: A PracticalGuide with ExamplesHOSTNEYhostney.comMarch 20, 2026

grep searches text for lines that match a pattern. You give it a string or regular expression, point it at a file or pipe, and it prints every line that matches. It is one of the most used commands on Linux, and for good reason: once you know grep, you can find anything in any text file on the system in seconds.

This guide covers grep’s syntax, every commonly used flag, regular expressions, piping grep with other commands, and practical examples for server administration and WordPress troubleshooting.

What grep does#

grep reads input line by line and prints lines that contain a match for the pattern you specify. The name stands for “globally search for a regular expression and print matching lines,” which is exactly what it does.

grep "error" /var/log/php/error.log

This prints every line in the file that contains the string “error.” The search is case-sensitive by default, so “Error” and “ERROR” are not matched.

Basic syntax#

grep [options] pattern [file...]
  • pattern is the string or regular expression to search for
  • file is one or more files to search. If no file is given, grep reads from standard input (a pipe)
  • options are flags that modify grep’s behavior
# Search a single file
grep "404" access.log

# Search multiple files
grep "404" access.log error.log

# Search with a pipe
cat access.log | grep "404"

When grep matches a line, it prints the entire line by default. When searching multiple files, grep prefixes each line with the filename so you know which file the match came from.

Commonly used flags#

Case insensitive search (-i)

grep -i "error" /var/log/syslog

Matches “error,” “Error,” “ERROR,” and any other case variation. Without -i , grep is case-sensitive.

This is one of the flags you will use constantly. Log files are inconsistent about capitalization. PHP logs write “Fatal error” while Nginx logs write “error” in lowercase. The -i flag catches both.

Show line numbers (-n)

grep -n "define" wp-config.php

Output:

23:define('DB_NAME', 'wordpress');
24:define('DB_USER', 'wp_user');
25:define('DB_PASSWORD', 'secretpassword');
26:define('DB_HOST', 'localhost');

The number before each line is the line number in the file. This is useful when you need to open the file in an editor and jump to a specific line.

Invert match (-v)

grep -v "GET /favicon.ico" access.log

Prints every line that does NOT match the pattern. This is how you filter out noise. If your access log is full of favicon requests and you want to see everything else, -v removes them.

Chain multiple inversions to filter out several patterns:

grep -v "favicon.ico" access.log | grep -v "robots.txt" | grep -v "wp-cron"

Count matches (-c)

grep -c "404" access.log

Prints the number of matching lines instead of the lines themselves. Useful for quick counts: how many 404s today, how many failed login attempts, how many times a specific error occurred.

Show only the matching part (-o)

grep -o "192\.168\.[0-9]*\.[0-9]*" access.log

By default, grep prints the entire line that contains a match. The -o flag prints only the matched text, one match per line. Useful when you want to extract specific values from lines.

Quiet mode (-q)

if grep -q "DISABLE_WP_CRON" wp-config.php; then
    echo "WP-Cron is disabled"
fi

The -q flag suppresses all output. grep still sets its exit code: 0 if a match was found, 1 if not. This makes it useful in shell scripts where you need to check whether a pattern exists without printing anything.

Show context around matches (-A, -B, -C)

# Show 3 lines after each match
grep -A 3 "Fatal error" error.log

# Show 2 lines before each match
grep -B 2 "Fatal error" error.log

# Show 2 lines before and after each match
grep -C 2 "Fatal error" error.log

Context lines help you understand what happened around an error. A “Fatal error” line by itself might not tell you much, but the lines before it often show the function call that triggered it.

Show filenames only (-l)

grep -l "eval(" /var/www/html/wp-content/plugins/*/*.php

Prints only the names of files that contain a match, not the matching lines. Useful when you are looking for which files contain a specific pattern across many files.

The opposite is -L , which prints filenames that do NOT contain the pattern.

Recursive search (-r)

grep -r "admin-ajax" /var/www/html/wp-content/plugins/

Searches all files in the directory and all subdirectories. Without -r , grep only searches the files you explicitly name. With -r , it walks the directory tree.

# Search the entire WordPress installation for a string
grep -r "malicious_function" /var/www/html/

Add -l to see only filenames:

grep -rl "eval(base64_decode" /var/www/html/

This is one of the most useful combinations for security scanning: find all PHP files that contain suspicious patterns like eval(base64_decode( , which is a common indicator of injected malware.

Fixed strings (-F)

grep -F "error_reporting(E_ALL)" config.php

Treats the pattern as a literal string, not a regular expression. Characters like . , * , ( , ) , [ , ] are matched literally instead of being interpreted as regex special characters.

Use -F when your search string contains regex characters and you want an exact match without needing to escape everything.

Word match (-w)

grep -w "error" logfile.txt

Matches “error” only as a whole word. Does not match “errors,” “error_log,” or “myerror.” The pattern must be bounded by non-word characters (spaces, punctuation, start/end of line).

Extended regular expressions (-E)

grep -E "error|warning|fatal" /var/log/syslog

Enables extended regular expressions, which support | (OR), + (one or more), ? (zero or one), and {} (repetition) without needing to escape them. Without -E , you need backslashes: grep "error\|warning\|fatal" .

-E is equivalent to using egrep .

grep with OR (matching multiple patterns)#

Using extended regex (-E)

grep -E "error|warning|critical" /var/log/php/error.log

Matches lines containing “error” OR “warning” OR “critical.”

Using multiple -e flags

grep -e "error" -e "warning" -e "critical" /var/log/php/error.log

Each -e specifies a separate pattern. A line matches if it contains any of them. This is functionally identical to the -E approach but does not require regex syntax.

grep with AND (matching multiple patterns on the same line)

grep does not have a built-in AND operator. Chain multiple grep commands with pipes:

grep "POST" access.log | grep "wp-login" | grep "403"

Each grep in the chain filters further. The result is lines that contain all three strings.

grep with pipes#

Piping is where grep becomes indispensable. Any command that produces text output can be piped into grep to filter for what you care about.

ps grep (find processes)

ps aux | grep php-fpm

Lists all running processes and filters for lines containing “php-fpm.” This is the standard way to check if a service is running and see its resource usage.

One annoyance: the grep command itself shows up in the results because grep php-fpm is also a running process that contains “php-fpm” in its command line. The common workaround:

ps aux | grep [p]hp-fpm

The brackets around the first letter make it a character class regex that still matches “php-fpm” but does not match the literal string “[p]hp-fpm” in the grep process itself.

ls grep (filter directory listings)

ls -la | grep "\.php$"

Lists directory contents and filters for PHP files. The $ anchors the match to the end of the line.

ls -la /var/log/ | grep "access"

Finds log files with “access” in the name.

docker logs grep

docker logs container_name 2>&1 | grep "error"

Docker writes logs to both stdout and stderr. The 2>&1 redirects stderr to stdout so grep can search both streams.

tail and grep

tail -f /var/log/nginx/error.log | grep "502"

Follows the log file in real time and prints only lines containing “502.” This is how you monitor for specific errors as they happen. Press Ctrl+C to stop.

dmesg grep

dmesg | grep -i "memory"

Searches kernel messages for memory-related entries. Useful for diagnosing hardware issues, out-of-memory kills, and disk errors.

history grep

history | grep "rsync"

Searches your command history for previous rsync commands. Faster than scrolling through hundreds of commands to find the one you ran last week.

Regular expressions in grep#

grep supports two flavors of regular expressions: basic (default) and extended ( -E ). The common patterns:

PatternMatches
. Any single character
* Zero or more of the preceding character
^ Start of line
$ End of line
[abc] Any one of a, b, or c
[a-z] Any lowercase letter
[0-9] Any digit
\ Escape the next character (treat it literally)

With -E (extended):

PatternMatches
+ One or more of the preceding character
? Zero or one of the preceding character
| OR (alternation)
{n} Exactly n of the preceding character
{n,m} Between n and m of the preceding character
() Grouping

Practical regex examples

Find lines starting with a specific string:

grep "^error" logfile.txt

Find empty lines:

grep "^$" file.txt

Find lines that end with a semicolon:

grep ";$" config.php

Find IP addresses (approximate):

grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" access.log

Find email addresses (approximate):

grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" file.txt

Find lines containing a PHP function call:

grep -E "function\s+[a-zA-Z_]+" *.php

Practical examples#

Find 404 errors in Nginx access logs

grep " 404 " /var/log/nginx/access.log

The spaces around 404 prevent matching URLs that happen to contain “404” as part of the path.

Count requests by HTTP status code

grep -c " 200 " access.log
grep -c " 301 " access.log
grep -c " 404 " access.log
grep -c " 500 " access.log

Find the most common 404 URLs

grep " 404 " access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

Extracts the URL from each 404 line, counts occurrences, and shows the top 20. This tells you which broken links or missing resources are being requested most.

Find failed SSH login attempts

grep "Failed password" /var/log/auth.log

Shows every failed SSH login attempt with the username and source IP.

Search WordPress files for malware indicators

grep -rl "eval(base64_decode" /var/www/html/wp-content/
grep -rl "exec(" /var/www/html/wp-content/uploads/
grep -rl "system(" /var/www/html/wp-content/uploads/

PHP files in the uploads directory are almost always malicious. Legitimate uploads are images, documents, and media files, not PHP scripts. The eval(base64_decode( pattern is a classic obfuscation technique used by malware to hide its payload.

Find which plugin generates the most admin-ajax.php requests

grep "admin-ajax" /var/log/nginx/access.log | grep -oE "action=[a-zA-Z0-9_]+" | sort | uniq -c | sort -rn | head -10

Extracts the action parameter from admin-ajax.php requests and shows which actions are called most frequently.

Search for a configuration value across all Nginx config files

grep -rn "client_max_body_size" /etc/nginx/

Finds every Nginx configuration file that sets or references the upload size limit, with line numbers.

Find PHP errors from the last hour

grep "$(date +'%d-%b-%Y %H')" /var/log/php/error.log

Filters PHP errors by the current hour’s timestamp. The date format depends on how your PHP error log is formatted.

Monitor a log file for specific errors in real time

tail -f /var/log/nginx/error.log | grep --line-buffered "upstream timed out"

The --line-buffered flag forces grep to flush output for each line instead of buffering. Without it, grep may buffer output when reading from a pipe, and you see matches in delayed chunks instead of in real time.

Find large log entries

grep -E ".{500,}" access.log

Matches lines longer than 500 characters. Unusually long request lines often indicate attack attempts (SQL injection, buffer overflow probes) where the attacker stuffs a payload into URL parameters.

Check if a string exists in a file (scripting)

if grep -q "DISABLE_WP_CRON" /var/www/html/wp-config.php; then
    echo "WP-Cron is already disabled"
else
    echo "WP-Cron is not disabled"
fi

The -q flag makes grep silent. The if statement checks grep’s exit code: 0 means a match was found, 1 means no match.

grep vs other search tools#

grep vs find

grep searches file contents. find searches file names and metadata (size, permissions, modification time). They solve different problems but work well together:

# find PHP files modified in the last 24 hours, then search their contents
find /var/www/html -name "*.php" -mtime -1 -exec grep -l "eval(" {} \;

For a complete guide to the find command, including combining find with grep, see the file and text searches via SSH tutorial.

grep vs awk

grep finds lines that match a pattern. awk processes and transforms text, splitting lines into fields and performing operations on them. Use grep when you need to find something. Use awk when you need to extract, calculate, or reformat data from what you found.

# grep: find 404 lines
grep " 404 " access.log

# awk: extract just the IP and URL from 404 lines
awk '$9 == 404 {print $1, $7}' access.log

grep vs sed

grep prints lines that match. sed edits text (find and replace, delete lines, insert text). They are complementary:

# Find lines containing the old domain, then replace it
grep -rn "old-domain.com" /var/www/html/
sed -i 's/old-domain.com/new-domain.com/g' /var/www/html/wp-config.php

grep on Hostney#

On Hostney, grep is available via SSH in your account container. Connect through the Terminal Access section in the control panel, then use grep to search your site files, PHP error logs, and access logs.

For running grep commands on your server without an interactive SSH session, see How to run commands over SSH. This is useful when you want to quickly check a log file from your local terminal without opening a persistent connection.