"Find the file where this string is mentioned" — the task comes up constantly. Where did traffic go, where is the old domain configured, which config file has a stale database password — content search answers all of it. There are several tools available, each with its own strengths.
grep — The Main Tool
grep stands for global regular expression print. It reads files and prints lines that match a pattern.
Basic syntax:
grep [options] "pattern" file_or_path
The most common server use case — recursive search through a directory:
grep -r "search_string" /path/to/directory
The -r flag makes grep traverse all subdirectories. Without it, grep only looks at the specified file or list of files.
Flags That Get Used in Practice
-i — case doesn't matter
grep -ri "error" /var/log/nginx/
With this flag, Error, ERROR and error are treated as identical. In practice, essential when searching logs where severity levels may be formatted differently.
-n — line numbers in output
grep -n "listen 80" /etc/nginx/nginx.conf
Shows exactly which line a match was found on. Useful when you need to open the file in an editor and jump directly to the right spot.
-l — filenames only
grep -rl "database_name" /etc/
Instead of printing matching lines — just a list of files where the pattern appeared. When there are many results and you need to understand the scope first.
-c — match count
grep -c "POST" /var/log/nginx/access.log
Outputs the number of matching lines rather than the lines themselves. A quick way to see how many POST requests are in the log without scrolling through it.
-v — inverted search
grep -v "127.0.0.1" /var/log/nginx/access.log
Shows lines where the pattern does not appear. Useful for filtering out local requests from a log to see only external traffic.
-A, -B, -C — context around a match
grep -A 3 "fatal error" /var/log/php/error.log
-A 3 prints 3 lines after each match. -B 3 — 3 lines before. -C 3 — 3 lines on both sides. Indispensable when reading stack traces in logs.
Flag Combinations for Real Tasks
Find all configs mentioning a specific IP
grep -rn "192.168.1.100" /etc/ --include="*.conf"
--include restricts the search to files with a specific extension. Without it, grep digs into binaries and everything irrelevant.
Search for multiple patterns at once
grep -E "error|warning|critical" /var/log/syslog
The -E flag enables extended regular expressions. The pipe character acts as logical OR — finds lines containing any of the three words.
Exclude certain files from search
grep -r "old_domain.com" /var/www/ --exclude="*.log" --exclude-dir=".git"
--exclude and --exclude-dir remove unwanted paths — logs, cache, git repositories.
Count unique IPs from an access log
grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
-o prints only the matched portion of each line, not the full line. Combined with sort and uniq, this produces a top-20 IP list ranked by request count.
Searching Compressed Logs: zgrep
Logs on servers are often rotated and compressed into .gz archives. Regular grep cannot read them — that is what zgrep is for:
zgrep "error" /var/log/nginx/error.log.2.gz
Works identically to grep but reads gzip archives directly. Same syntax, same flags.
To search across all rotated logs at once:
zgrep -h "critical" /var/log/nginx/error.log*.gz
-h suppresses filenames from the output — useful when only the line content matters.
ripgrep — A Faster Alternative
rg (ripgrep) is a modern replacement for grep, written in Rust. On large codebases and directories it runs significantly faster through parallel file traversal.
Installation:
apt install ripgrep
Basic usage follows the same syntax:
rg "string" /path
Key differences from grep:
- automatically ignores
.git,node_modules, and anything in.gitignore - recursive mode is on by default
- colored output with match highlighting out of the box
- supports compressed file search with the
-zflag
rg -z "error" /var/log/
For a one-off check the difference is negligible. On a project with thousands of files — substantial.
find + grep: When File Attribute Filtering Is Needed
Sometimes the search should be limited to files modified in the last 24 hours, or only to files under a certain size. Here grep needs help from find:
find /var/www -name "*.php" -mtime -1 | xargs grep -l "eval("
Finds .php files modified in the past day and checks each one for eval( — a classic indicator of injected malicious code.
find /etc -type f -size -10k | xargs grep -rn "password"
Searches for the word password only in files smaller than 10 kilobytes — cuts out large binaries and media files.
xargs passes find results as input to grep. Without it the command would be considerably more complex.
Quick Reference
| Task | Command |
|---|---|
| Recursive search through a directory | grep -r "string" /path/ |
| Case-insensitive search | grep -ri "string" /path/ |
| Show line numbers | grep -n "string" file |
| Filenames with matches only | grep -rl "string" /path/ |
| Inverted search | grep -v "string" file |
| Context around matches | grep -C 3 "string" file |
| Only .conf files | grep -r "string" /path/ --include="*.conf" |
| Search compressed logs | zgrep "string" file.gz |
| Multiple patterns | grep -E "a|b|c" file |
| Via find with filtering | find /path -name "*.php" | xargs grep "string" |
Summary
For most tasks grep -rn with the right flag covers everything. Compressed logs — zgrep. A large project with thousands of files — worth installing ripgrep. Search with date or size filtering — find piped into xargs grep.
All of this works on any Linux server without additional configuration. If a server is still needed — the PQ.Hosting catalog has options across a wide range of locations and configurations.