16 Practical grep Command Examples
Learn 16 practical GNU grep examples for literal text, regex, recursive searches, pipelines, context, scripts, and safe edge-case handling.
grep searches input for lines matching a pattern and prints the matching lines by default. Start by choosing whether your pattern is literal text (-F) or a regular expression (the default basic regex mode, or -E for extended regex), then choose the output you need: lines, line numbers, counts, filenames, context, or only the matched text.
The examples below use GNU grep 3.12 syntax. Options such as --include and some behavior described here are GNU extensions; check your platform’s manual when portability matters. See the GNU Grep manual and the POSIX grep specification.
Quick option guide
| Need | Option | Example |
|---|---|---|
| Ignore case | -i |
grep -i 'error' app.log |
| Literal text | -F |
grep -F 'price: $5.00' app.log |
| Extended regex | -E |
grep -E 'ERROR|FATAL' app.log |
| Line numbers | -n |
grep -n 'timeout' app.log |
| Invert matches | -v |
grep -v 'DEBUG' app.log |
| Count matching lines | -c |
grep -c 'WARN' app.log |
| Filenames only | -l |
grep -l 'main' ./*.c |
| Matched text only | -o |
grep -oE '[0-9]{3}-[0-9]{4}' contacts.txt |
| Nearby lines | -C 2 |
grep -C 2 'Exception' app.log |
| Recursive search | -r |
grep -r -n 'TODO' ./project |
16 practical grep examples
1. Find a simple string
grep 'ERROR' app.log
This prints every line in app.log containing ERROR. Patterns are regular expressions by default, but this pattern contains no regex metacharacters, so it behaves like a literal match.
2. Ignore case
grep -i 'error' app.log
-i matches error, Error, and ERROR. Use it for human-entered text or logs whose capitalization is inconsistent.
3. Search for literal punctuation
grep -F 'price: $5.00' app.log
-F selects fixed-string mode. Dollar signs, periods, brackets, and other punctuation are treated literally, which is usually clearest when searching for text copied from a log.
4. Match a whole word
grep -w 'cat' notes.txt
GNU grep’s -w checks word boundaries using its documented word-constituent rules, so it does not match cat inside concatenate. Test unusual punctuation and language-specific text against the exact input you have.
5. Match either of two alternatives
grep -E 'ERROR|FATAL' app.log
-E enables extended regular expressions, including the alternation operator |. Quote the pattern so the shell passes it unchanged.
6. Match the beginning of a line
grep '^2026-' app.log
The caret anchors the pattern to the start of each line. Replace 2026- with the prefix relevant to your data.
7. Show line numbers
grep -n 'timeout' app.log
-n adds each match’s one-based line number, which is useful when opening the file in an editor or reporting a failure.
8. Print only the matching part
grep -oE '[0-9]{3}-[0-9]{4}' contacts.txt
With GNU grep, -o prints each nonempty matched portion on its own line. This is useful for extracting identifiers from otherwise noisy lines.
9. Count matching lines
grep -c 'WARN' app.log
-c prints the number of matching lines. It counts lines, not every occurrence when a line contains the pattern more than once.
10. Print lines that do not match
grep -v 'DEBUG' app.log
-v inverts selection. This removes debug lines from a stream while retaining every other line.
11. List only filenames with a match
grep -l 'main' ./*.c
-l prints the name of each file containing at least one match, rather than printing matching content. Use -L to list files with no match.
12. Show surrounding context
grep -C 2 'Exception' app.log
-C 2 shows two lines before and after each matching line. Use -B 2 for before-only context or -A 2 for after-only context.
13. Search a directory tree
grep -r -n 'TODO' ./project
-r recursively visits files below ./project, and -n preserves useful line locations. Add -I when you want GNU grep to skip binary files.
14. Restrict recursive search to selected file types
grep -r -n --include='*.c' 'main' ./src
--include is a GNU option that limits recursive traversal results to matching filenames. For several types, repeat it:
grep -r -n --include='*.c' --include='*.h' 'main' ./src
For more precise selection, compose find with grep:
find ./src -type f \( -name '*.c' -o -name '*.h' \) -exec grep -nH 'main' {} +
15. Filter command output through a pipe
journalctl -u example.service | grep -i 'failed'
When no filename is supplied, grep reads standard input. The availability and permissions of journalctl depend on the operating system; substitute any command that produces text.
16. Safely handle patterns and filenames beginning with -
grep -e "$pattern" ./*
-e explicitly marks the next argument as a pattern, while ./* expands filenames with a path prefix so they are not mistaken for options. You can also use -- as an option terminator:
grep -- "$pattern" ./file.txt
The GNU manual documents a caveat for a file literally named -; use an explicit path such as ./- when that case matters.
Literal search versus regular expressions
Use -F when the input is an exact snippet. It avoids escaping regex punctuation and communicates intent. GNU grep’s default is basic regular expressions (-G); use -E for readable alternation, grouping, and repetition. GNU’s -P requests PCRE-style matching, but it is not a portable default and may be unavailable on another implementation.
| Mode | Best for | Portability |
|---|---|---|
-F |
Exact text and copied punctuation | POSIX |
default / -G |
Basic regular expressions | POSIX |
-E |
Alternation and extended expressions | POSIX |
-P |
PCRE-specific syntax | GNU-specific and implementation-dependent |
Shell quoting and filename safety
- Quote patterns such as
'*.c','$5.00', and'ERROR|FATAL'so the shell does not expand or reinterpret them. - Shell globs and grep regexes are separate syntaxes:
*.cselects filenames, while a grep pattern describes line content. - Use
-eor--when a pattern can begin with a hyphen. - Prefer
./fileor an absolute path for filenames beginning with-.
Exit statuses in scripts
GNU grep exits with status 0 when it selects at least one line, 1 when it selects none, and 2 for an error. A no-match result is therefore not the same as a failed command.
if grep -qF 'READY' status.txt; then
echo 'ready'
elif [ "$?" -eq 1 ]; then
echo 'not ready'
else
echo 'grep error' >&2
exit 2
fi
This distinction matters with set -e, where an expected no-match status can stop a script. Quiet mode has documented special behavior when a match is found even if an error was also detected, so check the manual when combining -q with large or changing inputs.
Performance and reliability checklist
- Use
-Ffor literal searches; it avoids regex parsing and accidental metacharacters. - Limit traversal with a starting directory,
--include, orfindrather than scanning an entire filesystem. - Use
-lwhen you only need filenames and-qwhen you only need a yes/no result. - Expect permission errors, unreadable files, binary data, changing files, and remote or mounted filesystem delays.
- Use
-nHin multi-file reports when both line numbers and filenames are needed. - For portable scripts, stick to POSIX options and verify GNU-only flags on the target host.
Troubleshooting common grep errors
| Symptom | Cause | Fix |
|---|---|---|
| No output | No line matched, or the pattern was altered by shell expansion | Check status $?, quote the pattern, and try -n or -F. |
grep: invalid option |
A pattern or filename starts with - |
Use -e "$pattern", --, or a ./ path. |
| Regex matches too much | Basic or extended regex metacharacters were unintended | Use -F for literal text and quote the pattern. |
| Recursive search misses files | Filename filter, permissions, symlink behavior, or binary content | Review --include, permissions, and whether the files are symlinks or binary. |
Binary file matches |
Input contains binary bytes | Decide whether to skip it, inspect it separately, or use an option appropriate to your GNU grep version. |
| Script stops on no match | set -e treats status 1 as failure |
Handle statuses explicitly as shown above. |
journalctl fails |
The command is unavailable or access is denied | Use another text-producing command or obtain the required permissions. |
Or skip the browser setup
If your next step is turning a URL into an image for documentation, tests, or an agent workflow, ScreenshotNeo provides a single GET request instead of maintaining browser automation. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
See the ScreenshotNeo API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Does grep search filenames or file contents?
It searches file contents. Shell globs, --include, and find determine which filenames are supplied.
What does grep print when nothing matches?
Normally nothing on standard output, with exit status 1. Treat that as a valid no-match result in scripts.
Should I use -r or -R?
Use the recursive option supported by your implementation and read its manual for symlink behavior. GNU-specific workflows should document the exact option and version.
Why did my dollar sign or brackets behave strangely?
The shell and regex engine both assign meaning to punctuation. Quote the pattern, then choose -F when you need an exact literal.


