Linux: Text Search — grep and Regular Expressions

Finding that one line among thousands of files — that's grep's specialty. It's probably the most frequently used command on Linux.

📋 Prerequisites: You should already know

1. What You'll Learn


2. A Story of Finding Code Across a Hundred Files

(1) The Pain Point: Changing an API Name, but Not Sure Which Files Use It

Xiaoming needed to change all calls from requests.get to httpx.get in Python files. The project had over 100 files — opening them one by one was impossible.

BASH
grep -rn "requests\.get" --include="*.py" src/

Output:

TEXT
src/api/client.py:15: response = requests.get(url, headers=headers)
src/api/users.py:42: data = requests.get(f"{API_URL}/users")
src/api/orders.py:78: result = requests.get(ORDER_URL)

3 files with calls — found in under a second.

(3) The Benefit: From Needle in a Haystack to Pinpoint Accuracy

Xiaoming formed a habit: before changing code, use grep -rn to find all related locations, confirm them, then start editing.


3. Knowledge Points

(1) grep Basic Usage

BASH
# Basic search
grep "pattern" filename

# Common option combinations
grep -rn "TODO" src/    # Recursive search, show line numbers
grep -i "error" log.txt # Ignore case
grep -v "debug" log.txt # Invert match (lines NOT containing debug)
grep -c "failed" log.txt # Count only
grep -l "main" *.py      # Show only matching filenames
grep -A5 -B5 "exception" log.txt # Show 5 lines before and after match
Option Meaning
-i Ignore case
-v Invert match (show non-matching lines)
-c Output only the count of matching lines
-n Show line numbers
-r or -R Recursive search through subdirectories
-l Show only filenames containing matches
-L Show only filenames NOT containing matches
-w Match whole words
-A NUM Show NUM lines after match
-B NUM Show NUM lines before match
-C NUM Show NUM lines before and after match

(2) Basic Regular Expressions

Metachar Meaning Example
. Match any single character h.t matches hit / hot / hat
^ Start of line ^root matches lines starting with root
$ End of line bash$ matches lines ending with bash
* Previous character repeated 0 or more times ab*c matches ac / abc / abbc
[abc] Match any character in the set [Hh]ello matches Hello or hello
[^abc] Match characters NOT in the set [^0-9] matches non-digit characters
\< Start of word \<root matches root at the start of a word
\> End of word root\> matches root at the end of a word
💡 Tip: The regex . matches any single character — if you need to match a literal dot (like in an IP address), you must escape it with a backslash: grep "192\.168\." file instead of grep "192.168." file (the latter would incorrectly match 192X168Y etc.).

(3) Extended Regular Expressions (grep -E)

In basic regex, +, ?, |, {}, () need escaping. Extended regex uses these metacharacters directly (via grep -E or egrep):

ℹ️ Note: The main difference between basic and extended regex is that in extended regex (grep -E), +, ?, |, {}, () can be used without backslash escaping. In basic regex, grep "error\|warning" requires escaping |, while grep -E "error|warning" does not. Prefer grep -E for clearer syntax.

Metachar Meaning Example
+ Previous character repeated 1 or more times ab+c matches abc / abbc but not ac
? Previous character appears 0 or 1 time colou?r matches color or colour
` ` Or
{n,m} Repeat n to m times [0-9]{3,5} matches 3-5 digit numbers
() Grouping `(error
BASH
# Search in a file
grep "root" /etc/passwd
# root:x:0:0:root:/root:/bin/bash

# Case-insensitive
echo -e "Error\nerror\nERROR" | grep -i "error"
# All three lines match

# Count
grep -c "nologin" /etc/passwd
# Output: 16

# Show line numbers
grep -n "www-data" /etc/passwd
# 33:www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

Output:

TEXT
root:x:0:0:root:/root:/bin/bash
Error
error
ERROR
16
33:www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

(5) ▶ Example: Invert Match

BASH
# Exclude comment lines
grep -v "^#" /etc/ssh/sshd_config | grep -v "^$"

# Exclude debug logs
grep -v "DEBUG" app.log | grep "ERROR"

# Exclude files with certain extensions
ls -la | grep -v "\.md$"

Output:

TEXT
Port 22
PermitRootLogin no
PasswordAuthentication no
2026-07-07 10:23:45 ERROR Database connection failed
2026-07-07 10:24:12 ERROR Timeout waiting for response
drwxr-xr-x 2 alice alice 4096 Jul  7 10:00 src
-rw-r--r-- 1 alice alice 1234 Jul  7 10:00 config.yaml
BASH
# Search for all TODO comments in the project
grep -rn "TODO\|FIXME\|HACK" --include="*.py" src/

# Search in specific file types
grep -rn "def " --include="*.py" --include="*.js" .

# Search excluding node_modules
grep -rn "api_key" --exclude-dir=node_modules --exclude-dir=.git .

# Show only filenames
grep -rl "class User" src/

Output:

TEXT
src/api/client.py:23:# TODO: Add retry logic
src/models/user.py:45:# FIXME: Handle edge case
src/utils/helpers.py:12:# HACK: Temporary workaround
src/api/client.py:10:def get_user(user_id):
src/models/user.py:5:def validate_email(email):
config/settings.py:3:api_key = "sk-xxx"
src/models/user.py
src/auth.py
💡 Tip: grep -rn is the golden combo for code search — -r recursively searches directories, -n shows line numbers, and --include limits file types. In large projects, grep -rn "pattern" --include="*.py" src/ can locate target code within a second.

(7) ▶ Example: Using Context

BASH
# Show 3 lines before and after the match
grep -C 3 "NullPointerException" app.log

# Show 5 lines after the match
grep -A 5 "Traceback" python_error.log

# Show 2 lines before the match
grep -B 2 "500" access.log

Output:

TEXT
10:23:01 INFO Processing request
10:23:02 WARN Memory usage high
10:23:03 ERROR NullPointerException at UserService.java:45
10:23:04 INFO Retrying operation
10:23:05 WARN Connection pool low
Traceback (most recent call last):
  File "app.py", line 42, in handle_request
    result = process(data)
  File "processor.py", line 15, in process
    return transform(input)
ValueError: Invalid input data
192.168.1.100 - - [07/Jul/2026:10:23:44] "POST /api/data"
192.168.1.100 - - [07/Jul/2026:10:23:45] "POST /api/data HTTP/1.1" 500 0

(8) ▶ Example: Regex in Practice

BASH
# Match email addresses
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" contacts.txt

# Match IP addresses
grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log

# Match URLs
grep -E "https?://[^ ]+" index.html

# Match dates (YYYY-MM-DD)
grep -E "[0-9]{4}-[0-9]{2}-[0-9]{2}" logs/*.log

# Match lines starting with a digit and ending with a letter
grep "^[0-9].*[a-zA-Z]$" file.txt

Output:

TEXT
alice@example.com
bob.smith@company.org
contact@my-site.net
192.168.1.100 - - [07/Jul/2026] ...
10.0.0.45 - - [07/Jul/2026] ...
https://example.com/api/users
http://internal.server.local/status
2026-07-07
2026-07-08
1abc
23x

(9) ▶ Comprehensive Example: Log Error Analysis

BASH
#!/bin/bash
# analyze-errors.sh - Log error analysis

LOG_FILE="${1:-/var/log/syslog}"

echo "=== Error Analysis Report ==="
echo "Source file: $LOG_FILE"
echo ""

echo "1. Error count by type:"
grep -i "error" "$LOG_FILE" | awk '{print $5}' | sort | uniq -c | sort -rn | head -10

echo ""
echo "2. Last 5 errors:"
grep -i "error" "$LOG_FILE" | tail -5

echo ""
echo "3. Critical alerts (OOM / Disk Full / Connection Refused):"
grep -iE "oom|out of memory|disk full|no space left|connection refused" "$LOG_FILE"

echo ""
echo "4. Error trend (by hour):"
grep -i "error" "$LOG_FILE" | grep -oP "\d{2}:\d{2}" | cut -d: -f1 | sort | uniq -c | sort -rn

❓ FAQ

Q: What's the difference between grep and regex? A: grep is a command-line tool for searching lines that match a pattern in files; regular expressions (regex) are a syntax for describing text patterns. grep supports regex as search patterns, but regex itself is just a syntax that can also be used in sed, awk, Python, and many other tools and languages.

Q: Are grep -r and grep -R the same? A: Basically yes. -R follows symbolic links, while -r does not by default. Either works fine for daily use.

Q: How do I search ignoring case? A: Use the -i option: grep -ir "error" /var/log/.

Q: Why does grep sometimes fail to find Chinese characters? A: This may be a locale issue. Chinese characters are multi-byte in UTF-8 encoding, and grep matches by byte by default. Try export LANG=zh_CN.UTF-8 or grep -P (Perl regex mode).

Q: What is grep -v useful for? A: Invert match — output lines that do NOT match the pattern. Combined with other grep calls, it can "exclude" certain lines, e.g., grep -v "^#" excludes comment lines, grep -v "^$" excludes blank lines.


📖 Summary


📝 Exercises

  1. Basic (⭐): Search /etc/passwd for users with /bin/bash as their login shell, then search a log file for errors and use -c to count them
  2. Intermediate (⭐⭐): Recursively search a project directory for all FIXME and TODO comments, then use grep -v to exclude the .git directory and count total lines of code
  3. Challenge (⭐⭐⭐): Use regex to match all IP addresses in a log file, then combine sort and uniq -c to count occurrences per IP and find the top 10 most frequent IPs
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏