grep prints lines that match a pattern, and its real power appears when the pattern is a regular expression rather than a fixed word. Regular expressions are a small language for describing text, and the same syntax is used by sed, awk, editors, and most programming languages. After this lesson you will be able to search whole codebases, pull structured data out of logs, and read the regex syntax you will keep meeting for the rest of your career.
| Option | Effect |
|---|---|
| -i | case-insensitive |
| -n | show line numbers |
| -r / -R | recurse into directories (-R follows symlinks) |
| -l / -L | list only filenames that match / do not match |
| -c | count matching lines per file |
| -v | invert: print lines that do not match |
| -w | match whole words only |
| -o | print only the matching part, one per line |
| -A n / -B n / -C n | lines of context after / before / around |
| -E | extended regular expressions |
| -F | fixed strings, no regex (fast, safe for special characters) |
| --include="*.js" / --exclude-dir=dist | filter files while recursing |
grep -rn --include="*.ts" "TODO" src/
grep -c "GET /api" access.log
grep -vE "^\s*(#|$)" /etc/ssh/sshd_config # config without comments and blank lines
grep -wl "password" *.envA regular expression is matched against each line. Most characters match themselves; a few are metacharacters with special meaning.
| Pattern | Matches |
|---|---|
| . | any single character |
| [abc], [a-z], [^0-9] | one character from a set, a range, or not in a set |
| ^, $ | start of line, end of line |
| * | zero or more of the preceding item |
| \. | a literal dot (escape any metacharacter with \) |
| \s, \w, \b | whitespace, word character, word boundary (GNU grep) |
grep "^root:" /etc/passwd # lines starting with root:
grep "sh$" /etc/passwd # lines ending in sh
grep "colou\?r" text.txt # basic regex: \? makes u optional
grep "[0-9][0-9]*" versions.txt # one or more digitsgrep defaults to basic regular expressions (BRE), where +, ?, {}, | and () are literal unless escaped. With -E (or the egrep alias) you get extended regular expressions (ERE), where they are metacharacters without escaping. Use -E by default; it is the syntax that JavaScript, Python and most tools share.
| ERE | Meaning |
|---|---|
| + | one or more |
| ? | zero or one |
| {3}, {2,5}, {2,} | exactly 3, 2 to 5, at least 2 |
| a\|b | alternation |
| (ab)+ | grouping |
grep -E "^(GET|POST) /api/v[0-9]+" access.log
grep -E "[0-9]{3}-[0-9]{4}" contacts.txt
grep -E "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[a-z]{2,}$" emails.txt
grep -oE "https?://[^ \"']+" page.html-o prints only the matched text, which turns grep into an extractor. Combined with sort and uniq it becomes a quick analytics tool.
# Top 5 IP addresses hitting the server
grep -oE "^[0-9]+(\.[0-9]+){3}" access.log | sort | uniq -c | sort -rn | head -5
# All distinct environment variable names referenced in a script
grep -oE '\$[A-Z_]+' deploy.sh | sort -ugrep -n -B 2 -A 5 "Traceback" app.log # 2 lines before, 5 after
grep -e "error" -e "warn" app.log # either pattern
grep -f patterns.txt app.log # patterns read from a file, one per line
grep -E "error|warn" app.log # same with alternationWhen the search text contains dots, brackets or dollar signs and you want them literal, -F avoids escaping altogether.
grep -F "config[env].url" app.js
grep -F -- "--force" install.sh # -- so "--force" is not treated as an optiongrep -r on a large repository is fast, but skip generated directories with --exclude-dir and consider ripgrep (rg), which respects .gitignore and is often several times quicker. Its flags (-i, -n, -o, -C) match grep, so nothing you learned here is wasted.
. matches any character: grep "1.0" also matches 100. Escape it: "1\.0".+ or | without -E and wondering why nothing matches.* or splits on spaces.^ and $ refer to the line, not the file.Which command prints only the filenames that contain the word "TODO"?
-rn for codebase searches, -i for case, -l for filenames, -c for counts and -C for context.-E so +, ?, {}, | and () work without backslashes.^, $, ., [] and * are the core metacharacters; escape them with \ to match literally.-o extracts matches, and grep -o | sort | uniq -c | sort -rn is a reusable analytics idiom.-F is the safe, fast choice for literal strings with special characters.Next lesson: Pipes, Redirection and Command Chaining — connect commands together and control where their input and output go.