Stage 2 · Tools
Text Processing
grep & ripgrep
Basic and extended regex, context lines, recursive search, and the power of ripgrep.
grep Fundamentals
grep searches input (files or stdin) for lines matching a pattern. It is one of the most frequently used commands in shell scripting — essential for log analysis, code search, and data filtering.
# Search for a literal string
grep "error" /var/log/syslog
# Case-insensitive search
grep -i "error" logfile.txt
# Invert match — show non-matching lines
grep -v "debug" logfile.txt
# Count matches
grep -c "404" access.log
# Show only matching part (not whole line)
grep -o "[0-9]+.[0-9]+.[0-9]+.[0-9]+" access.loggrep returns 0 if it finds matches, 1 if no matches found, and 2 on error. With set -e, a failed grep will exit your script — use || true to handle empty results.
With set -e, grep pattern file exits the script if the pattern is not found. Use grep pattern file || echo 'not found' or if grep -q pattern file; then to handle this gracefully.
Extended Regex
Basic grep uses BRE (basic regular expressions) where characters like +, ?, {, and | must be escaped. Extended grep (-E or egrep) makes these characters special without escaping.
| Pattern | BRE (grep) | ERE (grep -E) |
|---|---|---|
| One or more | a\+ | a+ |
| Zero or one | a\? | a? |
| Alternation | foo\|bar | foo|bar |
| Grouping | \(foo\) | (foo) |
| Character class | [0-9] | [0-9] (same) |
# Match IP addresses
grep -E "[0-9]+.[0-9]+.[0-9]+.[0-9]+" access.log
# Match email addresses
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}" contacts.txt
# Match HTTP status codes (200, 301, 404, 500)
grep -E "HTTP/[0-9.]" (200|301|404|500)" access.log
# Match dates (YYYY-MM-DD)
grep -E "[0-9]{4}-[0-9]{2}-[0-9]{2}" logfile.txtExtended regex is more readable for complex patterns. Use grep -E (or egrep) whenever you need alternation or quantifiers without backslash escaping.
Context Lines
When searching logs, you often need to see what happened before or after a matching line. grep provides -A (after), -B (before), and -C (context) for this.
# Show 3 lines after match
grep -A 3 "ERROR" logfile.txt
# Show 3 lines before match
grep -B 3 "ERROR" logfile.txt
# Show 3 lines before and after
grep -C 3 "ERROR" logfile.txt
# Group matches with -- separator
grep -A 3 --group-separator "ERROR" logfile.txtContext lines are invaluable for debugging. When you find an error, seeing the lines around it reveals the conditions that caused the problem.
Recursive Search
grep -r searches all files in a directory recursively. This is essential for searching codebases, configuration directories, or any collection of files.
# Search all files in current directory
grep -r "TODO" .
# Search only specific file types
grep -r --include="*.sh" "function" .
# Exclude directories
grep -r --exclude-dir=".git" "password" .
# Search with line numbers
grep -rn "error" /var/log/The --include and --exclude-dir options filter which files grep searches. This is useful for narrowing searches in large codebases or directories with binary files.
ripgrep (rg)
ripgrep is a modern alternative to grep that is faster, respects .gitignore, and has better defaults for searching code. It uses Rust's regex engine and is optimized for large codebases.
# Install
brew install ripgrep # macOS
sudo apt install ripgrep # Ubuntu
# Basic search (respects .gitignore by default)
rg "error"
# Search specific file types
rg -t py "import"
# Case-insensitive with context
rg -i -C 2 "warning"
# Fixed string search (no regex)
rg -F "hardcoded.string"
# Count matches per file
rg -c "TODO"ripgrep automatically skips .gitignore files, binary files, and hidden files. It is significantly faster than grep on large codebases and has better Unicode support.
Use rg for codebase searches — it is faster and has better defaults. Use grep in scripts and pipes where rg might not be available. Both are valuable tools.
Practical Patterns
Here are common grep patterns used in production shell scripts.
# Check if a process is running
pgrep -f "nginx" > /dev/null && echo "nginx is running"
# Extract unique IP addresses from a log
grep -oE "[0-9]+.[0-9]+.[0-9]+.[0-9]+" access.log | sort -u
# Find failed login attempts
grep "Failed password" /var/log/auth.log | wc -l
# Search for lines that match multiple conditions
grep -E "ERROR|FATAL" logfile.txt | grep -v "deprecated"
# Find TODO/FIXME in codebase
grep -rn "TODO|FIXME|HACK|XXX" --include="*.sh" .grep is often the first command in a pipeline. Combine it with sort, uniq, wc, and other tools to build powerful analysis pipelines.
When searching for literal text (not regex), use grep -F. It is significantly faster because it does not interpret special characters. Also available as the fgrep command.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.