06
Text Processing
- Filter ERROR lines from logs
- Top unique messages pipeline
- Quick field extract with awk/cut
Must-know cold
grep -i PATTERN FILE·grep -v·grep -c·grep -n·grep -E·grep -F·grep -r- Prefer
grep -E(ERE:+?|(){n,m}). Baregrepis BRE, not ERE. grep -Eo 'host=[^ ]+'·grep -En 'ERROR|WARN|FATAL'·grep -Ev 'level=INFO'sed -i.bak 's/old/new/g' FILE·sed -n '10,20p'awk '{print $1}'·awk -F',' '{print $2}'sort | uniq -c | sort -nr | head- Shape: extract → transform → aggregate
Commands
grep
Definition: Print lines matching a pattern (basic regex by default).
| Option | Argument | Meaning | Example |
|---|---|---|---|
| (none) | PATTERN FILE | Print lines matching PATTERN | grep ERROR app.log |
-i | PATTERN FILE | Case-insensitive | grep -i error app.log |
-v | PATTERN FILE | Invert; lines that do not match | grep -v INFO app.log |
-c | PATTERN FILE | Count matching lines | grep -c WARN app.log |
-n | PATTERN FILE | Show line numbers | grep -n ERROR app.log |
-E | PATTERN FILE | Extended regex (+, ?, |, (), {}) | grep -E 'ERROR|WARN' log |
-F | STRING FILE | Fixed string; no regex metacharacters | grep -F '1.2.3' log |
-w | PATTERN FILE | Whole word match | grep -w error log |
-x | PATTERN FILE | Whole line match | grep -x 'OK' log |
-r | PATTERN DIR | Recursive; follow command-line symlinks only (GNU) | grep -r ERROR /var/log |
-R | PATTERN DIR | Recursive; follow all symlinks (GNU) | grep -R ERROR /var/log |
-l | PATTERN PATH | Only filenames with matches | grep -rl ERROR /var/log |
-L | PATTERN PATH | Filenames without matches | grep -rL ERROR dir |
-o | PATTERN FILE | Only the matching part | grep -oE 'host=[^ ]+' log |
-A | N PATTERN FILE | N lines After match | grep -A2 ERROR log |
-B | N PATTERN FILE | N lines Before match | grep -B2 ERROR log |
-C | N PATTERN FILE | N lines Context both sides | grep -C3 ERROR log |
-q | PATTERN FILE | Quiet; exit status only (0=match) | grep -q ERROR log |
-e | PATTERN | Multiple patterns | grep -e A -e B log |
-f | FILE | Patterns from file | grep -f pats.txt log |
--include | GLOB | With -r: only matching names (GNU) | grep -r --include='*.log' ERR /var |
--exclude-dir | DIR | Skip directory (GNU) | grep -r --exclude-dir=.git PAT . |
-m | N PATTERN FILE | Stop after N matching lines (GNU) | grep -m1 ERROR log |
-h | — | No filename prefix (multi-file) | grep -h ERR *.log |
-H | — | Always print filename | grep -H ERR f |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
grep -RIn --exclude-dir=.git PAT . | Recursive, line nums, skip git | Code search |
grep -F -n 'literal.path' log | Safe for dots in paths/IPs | |
grep -E 'level=(ERROR|WARN)' log | Alternation | Prefer -E |
grep -q PAT f && echo found | Script gate | CI |
grep -E
Definition: grep with extended regex (
+,?,|,(),{n,m}) for flexible log/code search.
Why default to
-E: Basic regex makes+ ? | ( ) { }awkward or inert unless backslash-escaped. Extended regex is the syntax ops uses most.
vs-F: use-Ffor literal strings (paths, IPs, fixed messages).
vs-P: Perl regex is not portable; stick to-Efor scripts.
Invocation & high-value flag combos with -E
| Option / combo | Argument | Meaning | Example |
|---|---|---|---|
-E | PATTERN FILE | Enable extended regex | grep -E 'err|warn' log |
--extended-regexp | same | Long form of -E | grep --extended-regexp 'a+' f |
-Ei | PATTERN FILE | ERE + ignore case | grep -Ei 'error|fail' log |
-En | PATTERN FILE | ERE + line numbers | grep -En 'ERROR|FATAL' log |
-Eo | PATTERN FILE | ERE + only matching text | grep -Eo 'host=[^ ]+' log |
-Ev | PATTERN FILE | ERE + invert match | grep -Ev '^(#|$)' f |
-Ew | PATTERN FILE | ERE as whole words | grep -Ew 'error|warn' log |
-Ex | PATTERN FILE | ERE must match entire line | grep -Ex '[0-9]+' f |
-ERn | … | Recursive + line nums | grep -ERn 'TODO|FIXME' src/ |
-Eq | PATTERN FILE | Quiet; exit 0 if match | grep -Eq 'FAIL|ERROR' log |
-Ee | patterns | Multiple ERE patterns | grep -Ee 'ERR' -e 'WARN' log |
egrep | (legacy) | Often alias of grep -E | Prefer grep -E in scripts |
BRE vs ERE (what changes with -E)
| Feature | Basic (grep, GNU) | Extended (grep -E) | Example with -E |
|---|---|---|---|
| One or more | \+ (GNU; not POSIX BRE) | + | grep -E 'a+' |
| Zero or one | \? (GNU; not POSIX BRE) | ? | grep -E 'colou?r' |
| Alternation | backslash-pipe (GNU; POSIX BRE has none) | | | grep -E 'a|b' |
| Groups | \(\) | () | grep -E '(error|warn)' |
| Quantifier braces | \{n,m\} | {n,m} | grep -E '[0-9]{1,3}' |
Literal + ? ( ) { } and pipe | often bare | need \ escape | grep -E 'file\.txt' for dot |
Anchors & position
| Atom | Argument | Meaning | Example |
|---|---|---|---|
^ | — | Start of line | grep -E '^ERROR' log |
$ | — | End of line | grep -E 'failed$' log |
^$ | — | Empty line | grep -En '^$' f |
^PAT$ | — | Whole line (also -x) | grep -E '^[0-9]+$' f |
\< / \> | — | Word start/end (GNU) | grep -E '\<root\>' f |
\b | — | Word boundary (GNU) | grep -E '\berror\b' f |
Dot, classes, and escapes
| Atom | Argument | Meaning | Example |
|---|---|---|---|
. | — | Any one character (except newline) | grep -E 'a.c' f → abc / a-c |
\. | — | Literal dot | grep -E 'v1\.2\.3' f |
[abc] | — | One of a, b, c | grep -E 'level=[EW]' log |
[a-z] | — | Range | grep -E '[a-z]{4}' f |
[A-Za-z0-9_] | — | Word-ish class | identifiers |
[^ ] | — | Not space (one char) | token body |
[^abc] | — | Not a/b/c | grep -E '[^0-9]' f |
[[:space:]] | — | POSIX whitespace (space, tab, CR, FF, VT, NL) | grep -E '[[:space:]]+' f |
[[:blank:]] | — | Space and tab only | narrower than [[:space:]] |
[[:digit:]] | — | Decimal digits in the locale | [0-9] is ASCII 0–9 |
[[:alnum:]] | — | Alphanumeric | |
[[:alpha:]] | — | Letters | |
[[:xdigit:]] | — | Hex digits | SHAs, MACs |
\\ | — | Literal backslash | |
\t | — | Not a tab in GNU grep ERE (usually matches t) | use grep -P or [[:space:]] / a literal tab |
Quantifiers
| Atom | Argument | Meaning | Example |
|---|---|---|---|
* | — | Zero or more of previous | grep -E 'bo*t' f |
+ | — | One or more | grep -E 'err+' f |
? | — | Zero or one | grep -E 'https?' f |
{n} | — | Exactly n | grep -E '[0-9]{4}' f |
{n,} | — | n or more | grep -E '[A-Z]{3,}' f |
{n,m} | — | n to m inclusive | grep -E '[0-9]{1,3}' f |
*? +? | — | Non-greedy (not portable in ERE) | use [^ ]+ instead of .* |
Greedy tip for logs: prefer [^ ]+ / [^:]+ over .* so tokens stop at spaces.
Groups, alternation, backrefs
| Atom | Argument | Meaning | Example |
|---|---|---|---|
(…) | — | Group (for quantifiers/alternation) | grep -E '(ERROR|WARN)+' f |
| | — | Or | grep -E 'ERROR|FATAL|PANIC' log |
(a|b|c) | — | One of alternatives | grep -E 'level=(INFO|DEBUG)' log |
\1 \2 | — | Backreference to group (GNU grep) | grep -E '([0-9]+):\1' f same number twice |
(?:…) | — | Non-capturing group | not standard ERE — avoid |
Ops-ready pattern cookbook (grep -E)
| Goal | Pattern (inside single quotes) | Example |
|---|---|---|
| ERROR or WARN or FATAL | ERROR|WARN|FATAL | grep -En 'ERROR|WARN|FATAL' app.log |
| Case-insensitive fail words | fail|error|exception with -i | grep -Ei 'fail|error|exception' log |
| Log level field | level=(DEBUG|INFO|WARN|ERROR|FATAL) | grep -Eo 'level=(DEBUG|INFO|WARN|ERROR)' log |
| key=value host token | host=[^ ]+ | grep -Eo 'host=[^ ]+' log | sort -u |
| status=code | status=[0-9]{3} | grep -Eo 'status=[0-9]{3}' log |
| IPv4 (shape, not full validate) | ([0-9]{1,3}\.){3}[0-9]{1,3} | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' log |
| IPv4 with word bounds (GNU) | \b([0-9]{1,3}\.){3}[0-9]{1,3}\b | fewer false hits |
| Hex SHA-ish 7 or 40 | \b[0-9a-f]{7}\b|\b[0-9a-f]{40}\b | grep -Eio '…' log |
| UUID shape | [0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12} | grep -Eo '…' log |
| ISO date start | ^[0-9]{4}-[0-9]{2}-[0-9]{2} | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}' log |
Time HH:MM:SS | [0-2][0-9]:[0-5][0-9]:[0-5][0-9] | rough |
| Email shape (rough) | [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,} | triage only |
| Path under /var | /var/[^ ]+ | grep -Eo '/var/[^ ]+' log |
version v1.2.3 | v?[0-9]+\.[0-9]+\.[0-9]+ | grep -Eo 'v?[0-9]+\.[0-9]+\.[0-9]+' f |
| Trailing backslash lines | \\$ | grep -En '\\$' f |
| Non-comment non-empty | invert comments | grep -Ev '^[[:space:]]*(#|$)' f |
| Disk / OOM cues | No space left|Out of memory|oom-kill | grep -Ei 'no space left|out of memory|oom' log |
| Quoted message | msg="[^"]*" | grep -Eo 'msg="[^"]*"' log |
Extract → aggregate recipes (grep -E first)
| Goal | Command |
|---|---|
| Unique hosts from k=v logs | grep -Eo 'host=[^ ]+' app.log | sort -u |
| Count ERROR vs WARN | grep -Eo 'level=(ERROR|WARN)' app.log | sort | uniq -c |
| Top status codes | grep -Eo 'status=[0-9]{3}' access.log | sort | uniq -c | sort -nr |
| Unique IPv4s | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' log | sort -u |
| Lines with either code | grep -E 'E_CONN|E_TIME|E_DISK' log |
| Errors for one host (AND) | grep -E 'host=agent-01' log | grep -E 'level=ERROR' |
| Context around fatals | grep -En -C3 'FATAL|panic' log |
| Only filenames with TODO/FIXME | grep -ERl 'TODO|FIXME' --include='*.c' src/ |
| Script: fail if a hard error appears | if grep -Eq 'ERROR|FATAL' log; then exit 1; fi |
| Drop INFO/DEBUG noise | grep -Ev 'level=(INFO|DEBUG)' log |
Quoting rules for -E patterns
| Situation | Practice | Example |
|---|---|---|
Pattern has | or spaces | Single quotes | grep -E 'a|b' f |
| Need shell variable inside | Double quotes + careful escapes | grep -E "$user|root" f |
Literal * + ? ( | Escape with \ | grep -E 'file\*' f |
Literal . in version/IP | Escape each . or use -F | grep -E '10\.0\.0\.1' f |
Character class - | Put - first/last in [] | [-a-z] or [a-z-] |
Pitfalls specific to grep -E
grep -E 'file.txt'matchesfileXtxt— escape dots:file\.txtor use-F..*is greedy and crosses fields — prefer[^ ]+for log tokens.- Alternation has low precedence — use groups:
grep -E '^(ERROR|WARN):'not ambiguous forms. - Inside double quotes, the shell may eat
\— prefer single quotes for patterns. grep -Eis line-oriented; multi-line stack traces need flatten-first.-o+ alternation prints each alternative match separately (useful for counting tokens).- Character classes are not Unicode “letters” unless locale/PCRE — for ASCII logs stay with
[A-Za-z0-9]. \s/\ware GNU extensions ([[:space:]]/[_[:alnum:]]).\tis not a tab in GNUgrepERE.egrepis deprecated in some docs; writegrep -Ein scripts.
sed
Definition: Stream editor: filter or transform text line-by-line (substitute, delete, print ranges).
| Option | Argument | Meaning | Example |
|---|---|---|---|
| (none) | 'script' FILE | Stream edit; print result | sed 's/a/b/' f |
-n | 'script' FILE | Suppress auto-print | sed -n '1,5p' f |
-E / -r | 'script' FILE | Extended regex | sed -E 's/(a)/(b)/' f |
-i | 'script' FILE | In-place edit (no backup) | Dangerous |
-i.bak | 'script' FILE | In-place + backup suffix .bak | sed -i.bak 's/a/b/g' f |
-e | 'script' | Multiple expressions | sed -e 's/a/b/' -e '/^#/d' f |
-f | SCRIPTFILE FILE | Scripts from file | sed -f edits.sed f |
Common scripts (first column = program text):
| Script | Argument | Meaning | Example |
|---|---|---|---|
s/OLD/NEW/ | — | Replace first OLD per line | sed 's/ERROR/CRITICAL/' f |
s/OLD/NEW/g | — | Replace all per line | sed 's/foo/bar/g' f |
s|OLD|NEW|g | — | Alternate delimiter (URLs) | sed 's|http://|https://|g' f |
/PAT/d | — | Delete matching lines | sed '/^#/d' f |
/PAT/!d | — | Keep only matching | sed '/ERROR/!d' f |
Np with -n | — | Print line N | sed -n '10p' f |
N,Mp with -n | — | Print range | sed -n '50,75p' f |
/A/,/B/p | — | Print from A through B | sed -n '/START/,/END/p' f |
s/.*(PAT).*/\1/ | — | Capture group extract | sed -E 's/.*code=([^ ]+).*/\1/' f |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
sed -i.bak 's/old/new/g' f | Safe in-place | Always prefer backup |
sed -n '50,75p' large | Slice lines | No editor |
sed -E '/disk/ s/ERROR/SEVERE/' f | Address-limited sub |
awk
Definition: Pattern-scanning language for field-based reports, filters, and aggregation.
| Option | Argument | Meaning | Example |
|---|---|---|---|
| (none) | 'prog' FILE | Field-oriented processing | awk '{print $1}' f |
-F | FS 'prog' FILE | Field separator | awk -F',' '{print $2}' f |
-v | VAR=val 'prog' | Pass variable | awk -v n=3 '{print $n}' f |
-f | PROGFILE FILE | Program from file | awk -f p.awk data |
Program pieces:
| Snippet | Meaning | Example |
|---|---|---|
{print $1} | Field 1 (whitespace FS) | awk '{print $1}' access.log |
{print $NF} | Last field | |
$3=="ERROR" | Condition on field | awk '$3=="ERROR"' log |
NR>1 | Skip header line | awk -F, 'NR>1{print $1}' csv |
BEGIN{...} | Once before input | awk 'BEGIN{print "h"} {print}' |
END{...} | Once after input | awk '{c++} END{print c}' |
{c[$1]++} | Count by key | Group-by |
OFS="," | Output field sep |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
awk '{print $1}' | sort | uniq -c | Count keys | |
awk 'NR==FNR{a[$1]=1;next} $1 in a' allow data | Two-file allowlist | Advanced |
awk '{$1=$2=""; print}' | Blank fields 1–2; leaves leading OFS spaces | Not a clean cut — use a loop or cut |
cut
Definition: Extract selected columns/bytes from delimited lines.
| Option | Argument | Meaning | Example |
|---|---|---|---|
-d | DELIM | Field delimiter (default tab) | cut -d',' -f1 f.csv |
-f | LIST | Field numbers (1, 1,3, 2-4) | cut -d: -f1 /etc/passwd |
-c | LIST | Character positions | cut -c1-10 f |
-s | — | Skip lines without delimiter | cut -s -d, -f1 f |
--complement | — | Invert selected fields (GNU) | cut --complement -f1 |
--output-delimiter | STR | Output sep (GNU) | cut -d, -f1,2 --output-delimiter='|' |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
cut -d',' -f1 file | First CSV column | Simple only — no quoted commas |
Prefer awk when | Variable spaces / conditions |
sort
Definition: Sort lines of text (optionally numeric, by key, unique).
| Option | Argument | Meaning | Example |
|---|---|---|---|
| (none) | FILE | Sort lines ascending | sort f |
-r | — | Reverse | sort -r f |
-n | — | Numeric sort | sort -n f |
-h | — | Human numeric (2K, 1G) (GNU) | sort -h f |
-k | KEY | Sort key: 2,2 is field 2 only; 2 is field 2 through end of line | sort -k2,2 f |
-t | SEP | Field separator | sort -t: -k3 -n f |
-u | — | Unique lines (after sort) | sort -u f |
-b | — | Ignore leading blanks | sort -b f |
-f | — | Fold case | sort -f f |
-R | — | Random hash of keys (equal keys stay together; not shuf) (GNU) | sort -R f |
-o | FILE | Output to file (safe in-place style) | sort f -o f |
-c | — | Check if sorted; no output if ok | sort -c f |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
sort -nr | Numeric descending | Counts |
sort -k2,2nr | Field 2 only, numeric descending | sort -k2 -nr uses field 2 through EOL |
sort -t, -k1,1 file | CSV key |
uniq
Definition: Report or omit adjacent duplicate lines (usually after sort).
| Option | Argument | Meaning | Example |
|---|---|---|---|
| (none) | FILE | Drop adjacent duplicate lines | uniq f |
-c | — | Prefix count of adjacent dups | uniq -c f |
-d | — | Only duplicate lines | uniq -d f |
-u | — | Lines that occur exactly once (not “dedupe”) | uniq -u f |
-i | — | Ignore case | uniq -i f |
-f | N | Skip first N fields | uniq -f 1 f |
-s | N | Skip first N chars | uniq -s 10 f |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
sort | uniq -c | sort -nr | Frequency report | Classic |
Always sort first | Else uniq misses non-adjacent dups |
tr
Definition: Translate, squeeze, or delete characters from a stream.
| Option | Argument | Meaning | Example |
|---|---|---|---|
| (none) | SET1 SET2 | Translate chars SET1→SET2 | tr 'a-z' 'A-Z' |
-d | SET | Delete characters in SET | tr -d '\r' |
-s | SET | Squeeze repeats of SET | tr -s ' ' |
-c | SET1 SET2 | Complement SET1 | tr -cd '0-9\n' |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
tr -d '\r' < win.txt > unix.txt | CRLF → LF | |
tr -s ' ' < f | Collapse spaces | |
tr '[:lower:]' '[:upper:]' | Uppercase |
pipe
Definition: Connect stdout of the left command to stdin of the right.
| Operator | Argument | Meaning | Example |
|---|---|---|---|
| | — | stdout of left → stdin of right | grep E log | wc -l |
Common recipes
| Goal | Command |
|---|---|
| Errors only | grep ERROR app.log |
| Case-insensitive fail | grep -i fail app.log |
| Count WARN | grep -c WARN app.log |
| Top messages | grep ERROR app.log | awk '{$1=$2=$3=""; print}' | sort | uniq -c | sort -nr | head |
| Unique IPs (field 1) | awk '{print $1}' access.log | sort | uniq | wc -l |
| First column CSV | cut -d',' -f1 file.csv |
| Lines 50–75 | sed -n '50,75p' file |
| Replace in place | sed -i.bak 's/old/new/g' file |
| Context around error | grep -n -C3 ERROR app.log |
| Literal path with dots | grep -F '/opt/app/v1.2.3' log |
| ERROR or WARN (ERE) | grep -En 'ERROR|WARN' app.log |
| Extract hosts (ERE) | grep -Eo 'host=[^ ]+' app.log | sort -u |
| Drop INFO/DEBUG (ERE) | grep -Ev 'level=(INFO|DEBUG)' app.log |
Pitfalls
- Bare
grepis BRE.grep -E 'a|b'is alternation; GNU BRE writes that as backslash-pipe. POSIX BRE has no|. - GNU
grepERE does not treat\tas tab (\tusually matchest).\sis a GNU synonym for[[:space:]], not POSIX ERE. grep -randgrep -Rdiffer on GNU: only-Rfollows every symlink.grep -Eq PAT f && exit 1as the last line of a script exits 1 even when PAT is absent (grep’s own status). Useif grep -Eq …; then exit 1; fi.uniqwithoutsortonly collapses adjacent duplicates.uniq -ukeeps lines that appear once; it is notsort -u.sort -k2is field 2 through end of line; field 2 alone is-k2,2.sort -uwith-kuniques by the key, not the whole line.awk '{$1=$2=""; print}'leaves leading spaces; it does not delete the fields.- Unescaped
.in regex matches any character — usegrep -For\.. sed -iwithout backup is irreversible. GNU form is-i.bak; BSD/macOS wants-i ''/-i '.bak'.cut(and simpleawk -F,) break on quoted CSV commas — use a real CSV tool or Python.- Prefer
grep -Efor regex;grep -Ffor literals;grep -Pis GNU-only and not for portable scripts.
For more details, try man <command> in your terminal.