grep text
it's called 'searching.' you don't need an app for that.
The grep man page is 900 lines long. You will use maybe five flags for the rest of your career.
You wanted to find a string in a file. So you opened VS Code. All 400MB of it. It loaded for a while. You clicked the little magnifying glass in the sidebar. You typed your search term. You clicked the dropdown to expand “files to include.” You thought about a glob pattern. You typed *.log. You waited. You scrolled through results in a panel that’s three inches wide. You felt like a developer.
Or — and hear me out — you could have typed one command and had the answer before VS Code finished loading its telemetry.
Lazy sysadmins can skip straight to the cheat sheet.
Find a string in a file
This is the whole point. The reason grep exists. The thing you’ve been opening entire applications for.
grep "error" /var/log/syslog
That’s it. Every line containing “error” in that file. No sidebar. No magnifying glass icon. No “workspace trust” dialog.
Find a string in a bunch of files
The thing you opened an IDE for.
grep -r "TODO" /home/owner/projects/
-r means recursive. It searches every file in every subdirectory. You know how long it took your IDE to “index the workspace” before it could do this? grep doesn’t index anything. It just reads the files. Like you should have.
Find a string and show which file it’s in
You probably want this more than you think.
grep -rl "database_password" /etc/
-l only prints the filenames, not the matching lines. Great for when you know what you’re looking for but not where it is. Also great for finding out how many config files have hardcoded passwords in them. That number will haunt you.
Case-insensitive search
Because sometimes you don’t know if the person who wrote the log message thought “Error,” “ERROR,” or “error” was the right amount of panic.
grep -i "error" /var/log/syslog
-i ignores case. One flag. Your GUI search tool has a tiny Aa button you’ve been clicking for years. You’re welcome.
Show line numbers
So you can actually find the line after grep tells you it exists.
grep -n "connection refused" /var/log/app.log
-n prepends the line number. Now you can jump straight there instead of scrolling through the file like you’re reading the morning paper.
Show context around matches
The match is useless without knowing what’s around it.
grep -C 3 "segfault" /var/log/syslog
-C 3 shows 3 lines before and 3 lines after each match. -B 3 for only before, -A 3 for only after. Your IDE shows this in a tiny preview window that you have to hover over. grep just prints it. On your screen. Like text. Revolutionary.
Search for multiple things at once
grep -E "error|warning|critical" /var/log/syslog
-E enables extended regex. Now you’re searching for three things in one command. Your IDE would need three separate searches or a regex checkbox that you’d have to enable and then Google the syntax for anyway.
Invert the search
Find everything that does NOT match. Surprisingly useful.
grep -v "DEBUG" /var/log/app.log
-v inverts the match. Filter out all the noise and keep the stuff that matters. Try doing that in Notepad++. Actually, don’t. You’ll be there all day.
Count matches
Don’t need the lines, just need to know how bad it is?
grep -c "404" /var/log/access.log
-c counts matching lines. One number. Not a “Results: 2,847 matches found in 156 files” dialog box with an OK button.
Pipe it
This is where grep becomes a superpower. It’s not just for files — it filters the output of any command.
ps aux | grep nginx
docker ps | grep running
cat /etc/passwd | grep bash
history | grep rsync
Take any command that spews too much output and pipe it through grep. This is the Unix philosophy in one character: |. Your GUI tools can’t do this because they exist in their own little universe where nothing talks to anything else.
Dealing with the output
So grep found 4,000 matches and your terminal just had a seizure. Congratulations. Here’s how to not drown in your own results.
Page through results
grep -r "error" /var/log/ | less
less lets you scroll through results like a civilized person. Arrow keys to move, / to search within the results (yes, you can search your search), q to quit. It’s grep for your grep.
Save it to a file
grep -rn "FATAL" /var/log/ > fatal_errors.txt
> redirects output to a file. Now you have a record. Send it to your manager. CC the person who said “it’s probably fine.”
Only show the first few matches
grep -r "error" /var/log/ | head -20
head -20 shows the first 20 matches and stops. Because sometimes you just need to confirm that yes, it’s broken — you don’t need to see all 8,000 ways it’s broken.
Chain greps together
This is where it gets beautiful. Filter the output of grep with another grep.
grep -r "error" /var/log/ | grep -v "DEBUG" | grep -i "database"
That’s: find all errors, remove the debug noise, then narrow it to database issues. Three pipes. Zero GUI tools. You just built a custom log analysis pipeline in one line that would take a Splunk query and a meeting to accomplish.
Sort and deduplicate
grep -r "Failed login" /var/log/auth.log | sort | uniq -c | sort -rn | head -10
That monstrosity finds failed logins, sorts them, counts duplicates, sorts by count descending, and shows the top 10. You just did threat hunting in one line. Your SIEM costs $40,000 a year.
The flags that actually matter
| Flag | What it does |
|---|---|
-r |
Recursive. Search all files in all subdirectories. |
-i |
Case-insensitive. Because capitalization is a suggestion. |
-n |
Show line numbers. So you can find the line after grep finds it for you. |
-l |
Show only filenames. When you need the “where” not the “what.” |
-c |
Count matches. Just the number. |
-v |
Invert match. Show lines that DON’T match. |
-C N |
Show N lines of context around each match. -A for after, -B for before. |
-E |
Extended regex. For or patterns and fancier matching. |
--color |
Highlight matches. Usually on by default. If not, your terminal is sad. |
“But my IDE has a search—”
Sure does. Here’s what it costs you:
“VS Code has search across files.” VS Code uses 500MB+ of RAM at idle. grep uses about 1MB. You’re burning half a gig of memory so a magnifying glass icon can do what a 50-year-old command does faster.
“But I can click on the result and jump to the file.” Cool. grep -n gives you the file and line number. Open it with vim +42 file.txt and you’re on line 42. No mouse required. No “workspace trust” dialog.
“Agent Ransack has a nice interface.” Agent Ransack is a 15MB download to search text in files. Text. In files. It has a toolbar with icons. It has dropdown menus. It has a “containing text” field AND a “file name” field AND a “look in” field. You know what grep has? Arguments. That you type. Because you’re a grown-up.
“But regex is hard.” You know what’s harder? Explaining to your team why you installed a search application on the production server because you couldn’t remember how to use grep.
Cheat sheet
You made it. Or you skipped straight here. Either way, no judgment. Copy and paste these. Pin them. Tattoo them on your forearm. Whatever works.
| What you’re doing | Command |
|---|---|
| Search a file | grep "pattern" file.txt |
| Search recursively | grep -r "pattern" /path/ |
| Case-insensitive search | grep -i "pattern" file.txt |
| Show line numbers | grep -n "pattern" file.txt |
| Show only filenames | grep -rl "pattern" /path/ |
| Show context (3 lines) | grep -C 3 "pattern" file.txt |
| Search for multiple patterns | grep -E "this|that" file.txt |
| Invert (exclude matches) | grep -v "noise" file.txt |
| Count matches | grep -c "pattern" file.txt |
| Filter command output | command | grep "pattern" |
| Page through results | grep "pattern" file | less |
| Save results to file | grep "pattern" file > results.txt |
| First 20 matches only | grep "pattern" file | head -20 |
| Chain filters | grep "err" file | grep -v "DEBUG" | grep -i "db" |
| Top 10 by frequency | grep "pattern" file | sort | uniq -c | sort -rn | head -10 |