Linux Basic — grep -E in Scripts

2 minute read

grep -E in Scripts

grep -E (or its alias egrep) interprets your pattern as ERE — the same dialect awk and Bash’s =~ use. For scripting it’s almost always what you want.

grep -E '^(ERROR|FATAL): ' app.log

Script-Friendly Flags

Flag Meaning Why scripts love it
-E extended regex + ? ( ) \| without backslashes
-q quiet — exit 0 if match, 1 if not clean test in if / &&
-c count of matching lines quick metrics
-o print only the matched substring extract data
-n prefix with line number reports
-i case-insensitive flexible matching
-v invert match filter out
-r / -R recurse into directories search trees
-l list filenames only feed into xargs
-F fixed-string (no regex) match literal *.log

The Idiomatic Test

if grep -Eq '^https?://' <<< "$url"; then
    echo "looks like a URL"
fi
  • -E enables ERE
  • -q suppresses output (we only want the exit code)
  • here-string sends the variable as stdin without a subshell

This is the canonical “does this string match a pattern?” pattern in Bash.


Extract with -o

-o prints only the matched substring, one per line. Great for picking values out of structured text:

# pull every IPv4-ish token from a log
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort -u

# pull email addresses
grep -Eoi '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}' contacts.txt

Combine with head, wc -l, or sort | uniq -c to summarise.


Count Matches

err_count=$(grep -Ec '^ERROR' app.log)
echo "errors: $err_count"

-c counts lines, not occurrences — a line with two matches counts once. Use grep -Eo ... | wc -l for per-match counting.


Anchor Patterns to Avoid False Positives

# BAD — matches "10.20.30.40.567"
grep -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' input

# BETTER — word boundary on both sides
grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' input

\b is a GNU extension but widely available.


Pipelines

# only errors, then count by type
grep -E '^(ERROR|FATAL):' app.log \
  | awk -F: '{print $1}' \
  | sort | uniq -c | sort -rn

# files containing a pattern, opened for editing
grep -Erl '^TODO:' src/ | xargs -r $EDITOR

xargs -r is critical — it skips the call if input is empty (otherwise xargs runs the command with no args).


Pitfalls

Pitfall Fix
Forgetting -E and using \(...\) weirdly Just use -E
grep "..." inside an if prints noise Add -q
grep -c on multi-match line gives wrong number Use grep -o ... \| wc -l
Glob grep "*.log" instead of regex Use -F, or quote '\.log$'
Searching binaries by accident grep -I skips binary files

Next → Bash =~ Operator

Updated: