Kiet Nguyen logo
NotesNotesResumeResume
© 2026 Kiet Nguyen
← All categories

06

Text Processing

  • Filter ERROR lines from logs
  • Top unique messages pipeline
  • Quick field extract with awk/cut
grepgrep -Esedawkcutsortuniqtrpipe

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}). Bare grep is 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).

OptionArgumentMeaningExample
(none)PATTERN FILEPrint lines matching PATTERNgrep ERROR app.log
-iPATTERN FILECase-insensitivegrep -i error app.log
-vPATTERN FILEInvert; lines that do not matchgrep -v INFO app.log
-cPATTERN FILECount matching linesgrep -c WARN app.log
-nPATTERN FILEShow line numbersgrep -n ERROR app.log
-EPATTERN FILEExtended regex (+, ?, |, (), {})grep -E 'ERROR|WARN' log
-FSTRING FILEFixed string; no regex metacharactersgrep -F '1.2.3' log
-wPATTERN FILEWhole word matchgrep -w error log
-xPATTERN FILEWhole line matchgrep -x 'OK' log
-rPATTERN DIRRecursive; follow command-line symlinks only (GNU)grep -r ERROR /var/log
-RPATTERN DIRRecursive; follow all symlinks (GNU)grep -R ERROR /var/log
-lPATTERN PATHOnly filenames with matchesgrep -rl ERROR /var/log
-LPATTERN PATHFilenames without matchesgrep -rL ERROR dir
-oPATTERN FILEOnly the matching partgrep -oE 'host=[^ ]+' log
-AN PATTERN FILEN lines After matchgrep -A2 ERROR log
-BN PATTERN FILEN lines Before matchgrep -B2 ERROR log
-CN PATTERN FILEN lines Context both sidesgrep -C3 ERROR log
-qPATTERN FILEQuiet; exit status only (0=match)grep -q ERROR log
-ePATTERNMultiple patternsgrep -e A -e B log
-fFILEPatterns from filegrep -f pats.txt log
--includeGLOBWith -r: only matching names (GNU)grep -r --include='*.log' ERR /var
--exclude-dirDIRSkip directory (GNU)grep -r --exclude-dir=.git PAT .
-mN PATTERN FILEStop after N matching lines (GNU)grep -m1 ERROR log
-h—No filename prefix (multi-file)grep -h ERR *.log
-H—Always print filenamegrep -H ERR f

Flag combos

ComboMeaningExample
grep -RIn --exclude-dir=.git PAT .Recursive, line nums, skip gitCode search
grep -F -n 'literal.path' logSafe for dots in paths/IPs
grep -E 'level=(ERROR|WARN)' logAlternationPrefer -E
grep -q PAT f && echo foundScript gateCI

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 -F for literal strings (paths, IPs, fixed messages).
vs -P: Perl regex is not portable; stick to -E for scripts.

Invocation & high-value flag combos with -E

Option / comboArgumentMeaningExample
-EPATTERN FILEEnable extended regexgrep -E 'err|warn' log
--extended-regexpsameLong form of -Egrep --extended-regexp 'a+' f
-EiPATTERN FILEERE + ignore casegrep -Ei 'error|fail' log
-EnPATTERN FILEERE + line numbersgrep -En 'ERROR|FATAL' log
-EoPATTERN FILEERE + only matching textgrep -Eo 'host=[^ ]+' log
-EvPATTERN FILEERE + invert matchgrep -Ev '^(#|$)' f
-EwPATTERN FILEERE as whole wordsgrep -Ew 'error|warn' log
-ExPATTERN FILEERE must match entire linegrep -Ex '[0-9]+' f
-ERn…Recursive + line numsgrep -ERn 'TODO|FIXME' src/
-EqPATTERN FILEQuiet; exit 0 if matchgrep -Eq 'FAIL|ERROR' log
-EepatternsMultiple ERE patternsgrep -Ee 'ERR' -e 'WARN' log
egrep(legacy)Often alias of grep -EPrefer grep -E in scripts

BRE vs ERE (what changes with -E)

FeatureBasic (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'
Alternationbackslash-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 pipeoften bareneed \ escapegrep -E 'file\.txt' for dot

Anchors & position

AtomArgumentMeaningExample
^—Start of linegrep -E '^ERROR' log
$—End of linegrep -E 'failed$' log
^$—Empty linegrep -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

AtomArgumentMeaningExample
.—Any one character (except newline)grep -E 'a.c' f → abc / a-c
\.—Literal dotgrep -E 'v1\.2\.3' f
[abc]—One of a, b, cgrep -E 'level=[EW]' log
[a-z]—Rangegrep -E '[a-z]{4}' f
[A-Za-z0-9_]—Word-ish classidentifiers
[^ ]—Not space (one char)token body
[^abc]—Not a/b/cgrep -E '[^0-9]' f
[[:space:]]—POSIX whitespace (space, tab, CR, FF, VT, NL)grep -E '[[:space:]]+' f
[[:blank:]]—Space and tab onlynarrower than [[:space:]]
[[:digit:]]—Decimal digits in the locale[0-9] is ASCII 0–9
[[:alnum:]]—Alphanumeric
[[:alpha:]]—Letters
[[:xdigit:]]—Hex digitsSHAs, MACs
\\—Literal backslash
\t—Not a tab in GNU grep ERE (usually matches t)use grep -P or [[:space:]] / a literal tab

Quantifiers

AtomArgumentMeaningExample
*—Zero or more of previousgrep -E 'bo*t' f
+—One or moregrep -E 'err+' f
?—Zero or onegrep -E 'https?' f
{n}—Exactly ngrep -E '[0-9]{4}' f
{n,}—n or moregrep -E '[A-Z]{3,}' f
{n,m}—n to m inclusivegrep -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

AtomArgumentMeaningExample
(…)—Group (for quantifiers/alternation)grep -E '(ERROR|WARN)+' f
|—Orgrep -E 'ERROR|FATAL|PANIC' log
(a|b|c)—One of alternativesgrep -E 'level=(INFO|DEBUG)' log
\1 \2—Backreference to group (GNU grep)grep -E '([0-9]+):\1' f same number twice
(?:…)—Non-capturing groupnot standard ERE — avoid

Ops-ready pattern cookbook (grep -E)

GoalPattern (inside single quotes)Example
ERROR or WARN or FATALERROR|WARN|FATALgrep -En 'ERROR|WARN|FATAL' app.log
Case-insensitive fail wordsfail|error|exception with -igrep -Ei 'fail|error|exception' log
Log level fieldlevel=(DEBUG|INFO|WARN|ERROR|FATAL)grep -Eo 'level=(DEBUG|INFO|WARN|ERROR)' log
key=value host tokenhost=[^ ]+grep -Eo 'host=[^ ]+' log | sort -u
status=codestatus=[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}\bfewer false hits
Hex SHA-ish 7 or 40\b[0-9a-f]{7}\b|\b[0-9a-f]{40}\bgrep -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.3v?[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-emptyinvert commentsgrep -Ev '^[[:space:]]*(#|$)' f
Disk / OOM cuesNo space left|Out of memory|oom-killgrep -Ei 'no space left|out of memory|oom' log
Quoted messagemsg="[^"]*"grep -Eo 'msg="[^"]*"' log

Extract → aggregate recipes (grep -E first)

GoalCommand
Unique hosts from k=v logsgrep -Eo 'host=[^ ]+' app.log | sort -u
Count ERROR vs WARNgrep -Eo 'level=(ERROR|WARN)' app.log | sort | uniq -c
Top status codesgrep -Eo 'status=[0-9]{3}' access.log | sort | uniq -c | sort -nr
Unique IPv4sgrep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' log | sort -u
Lines with either codegrep -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 fatalsgrep -En -C3 'FATAL|panic' log
Only filenames with TODO/FIXMEgrep -ERl 'TODO|FIXME' --include='*.c' src/
Script: fail if a hard error appearsif grep -Eq 'ERROR|FATAL' log; then exit 1; fi
Drop INFO/DEBUG noisegrep -Ev 'level=(INFO|DEBUG)' log

Quoting rules for -E patterns

SituationPracticeExample
Pattern has | or spacesSingle quotesgrep -E 'a|b' f
Need shell variable insideDouble quotes + careful escapesgrep -E "$user|root" f
Literal * + ? (Escape with \grep -E 'file\*' f
Literal . in version/IPEscape each . or use -Fgrep -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' matches fileXtxt — escape dots: file\.txt or 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 -E is 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 / \w are GNU extensions ([[:space:]] / [_[:alnum:]]). \t is not a tab in GNU grep ERE.
  • egrep is deprecated in some docs; write grep -E in scripts.

sed

Definition: Stream editor: filter or transform text line-by-line (substitute, delete, print ranges).

OptionArgumentMeaningExample
(none)'script' FILEStream edit; print resultsed 's/a/b/' f
-n'script' FILESuppress auto-printsed -n '1,5p' f
-E / -r'script' FILEExtended regexsed -E 's/(a)/(b)/' f
-i'script' FILEIn-place edit (no backup)Dangerous
-i.bak'script' FILEIn-place + backup suffix .baksed -i.bak 's/a/b/g' f
-e'script'Multiple expressionssed -e 's/a/b/' -e '/^#/d' f
-fSCRIPTFILE FILEScripts from filesed -f edits.sed f

Common scripts (first column = program text):

ScriptArgumentMeaningExample
s/OLD/NEW/—Replace first OLD per linesed 's/ERROR/CRITICAL/' f
s/OLD/NEW/g—Replace all per linesed 's/foo/bar/g' f
s|OLD|NEW|g—Alternate delimiter (URLs)sed 's|http://|https://|g' f
/PAT/d—Delete matching linessed '/^#/d' f
/PAT/!d—Keep only matchingsed '/ERROR/!d' f
Np with -n—Print line Nsed -n '10p' f
N,Mp with -n—Print rangesed -n '50,75p' f
/A/,/B/p—Print from A through Bsed -n '/START/,/END/p' f
s/.*(PAT).*/\1/—Capture group extractsed -E 's/.*code=([^ ]+).*/\1/' f

Flag combos

ComboMeaningExample
sed -i.bak 's/old/new/g' fSafe in-placeAlways prefer backup
sed -n '50,75p' largeSlice linesNo editor
sed -E '/disk/ s/ERROR/SEVERE/' fAddress-limited sub

awk

Definition: Pattern-scanning language for field-based reports, filters, and aggregation.

OptionArgumentMeaningExample
(none)'prog' FILEField-oriented processingawk '{print $1}' f
-FFS 'prog' FILEField separatorawk -F',' '{print $2}' f
-vVAR=val 'prog'Pass variableawk -v n=3 '{print $n}' f
-fPROGFILE FILEProgram from fileawk -f p.awk data

Program pieces:

SnippetMeaningExample
{print $1}Field 1 (whitespace FS)awk '{print $1}' access.log
{print $NF}Last field
$3=="ERROR"Condition on fieldawk '$3=="ERROR"' log
NR>1Skip header lineawk -F, 'NR>1{print $1}' csv
BEGIN{...}Once before inputawk 'BEGIN{print "h"} {print}'
END{...}Once after inputawk '{c++} END{print c}'
{c[$1]++}Count by keyGroup-by
OFS=","Output field sep

Flag combos

ComboMeaningExample
awk '{print $1}' | sort | uniq -cCount keys
awk 'NR==FNR{a[$1]=1;next} $1 in a' allow dataTwo-file allowlistAdvanced
awk '{$1=$2=""; print}'Blank fields 1–2; leaves leading OFS spacesNot a clean cut — use a loop or cut

cut

Definition: Extract selected columns/bytes from delimited lines.

OptionArgumentMeaningExample
-dDELIMField delimiter (default tab)cut -d',' -f1 f.csv
-fLISTField numbers (1, 1,3, 2-4)cut -d: -f1 /etc/passwd
-cLISTCharacter positionscut -c1-10 f
-s—Skip lines without delimitercut -s -d, -f1 f
--complement—Invert selected fields (GNU)cut --complement -f1
--output-delimiterSTROutput sep (GNU)cut -d, -f1,2 --output-delimiter='|'

Flag combos

ComboMeaningExample
cut -d',' -f1 fileFirst CSV columnSimple only — no quoted commas
Prefer awk whenVariable spaces / conditions

sort

Definition: Sort lines of text (optionally numeric, by key, unique).

OptionArgumentMeaningExample
(none)FILESort lines ascendingsort f
-r—Reversesort -r f
-n—Numeric sortsort -n f
-h—Human numeric (2K, 1G) (GNU)sort -h f
-kKEYSort key: 2,2 is field 2 only; 2 is field 2 through end of linesort -k2,2 f
-tSEPField separatorsort -t: -k3 -n f
-u—Unique lines (after sort)sort -u f
-b—Ignore leading blankssort -b f
-f—Fold casesort -f f
-R—Random hash of keys (equal keys stay together; not shuf) (GNU)sort -R f
-oFILEOutput to file (safe in-place style)sort f -o f
-c—Check if sorted; no output if oksort -c f

Flag combos

ComboMeaningExample
sort -nrNumeric descendingCounts
sort -k2,2nrField 2 only, numeric descendingsort -k2 -nr uses field 2 through EOL
sort -t, -k1,1 fileCSV key

uniq

Definition: Report or omit adjacent duplicate lines (usually after sort).

OptionArgumentMeaningExample
(none)FILEDrop adjacent duplicate linesuniq f
-c—Prefix count of adjacent dupsuniq -c f
-d—Only duplicate linesuniq -d f
-u—Lines that occur exactly once (not “dedupe”)uniq -u f
-i—Ignore caseuniq -i f
-fNSkip first N fieldsuniq -f 1 f
-sNSkip first N charsuniq -s 10 f

Flag combos

ComboMeaningExample
sort | uniq -c | sort -nrFrequency reportClassic
Always sort firstElse uniq misses non-adjacent dups

tr

Definition: Translate, squeeze, or delete characters from a stream.

OptionArgumentMeaningExample
(none)SET1 SET2Translate chars SET1→SET2tr 'a-z' 'A-Z'
-dSETDelete characters in SETtr -d '\r'
-sSETSqueeze repeats of SETtr -s ' '
-cSET1 SET2Complement SET1tr -cd '0-9\n'

Flag combos

ComboMeaningExample
tr -d '\r' < win.txt > unix.txtCRLF → LF
tr -s ' ' < fCollapse spaces
tr '[:lower:]' '[:upper:]'Uppercase

pipe

Definition: Connect stdout of the left command to stdin of the right.

OperatorArgumentMeaningExample
|—stdout of left → stdin of rightgrep E log | wc -l

Common recipes

GoalCommand
Errors onlygrep ERROR app.log
Case-insensitive failgrep -i fail app.log
Count WARNgrep -c WARN app.log
Top messagesgrep 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 CSVcut -d',' -f1 file.csv
Lines 50–75sed -n '50,75p' file
Replace in placesed -i.bak 's/old/new/g' file
Context around errorgrep -n -C3 ERROR app.log
Literal path with dotsgrep -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 grep is BRE. grep -E 'a|b' is alternation; GNU BRE writes that as backslash-pipe. POSIX BRE has no |.
  • GNU grep ERE does not treat \t as tab (\t usually matches t). \s is a GNU synonym for [[:space:]], not POSIX ERE.
  • grep -r and grep -R differ on GNU: only -R follows every symlink.
  • grep -Eq PAT f && exit 1 as the last line of a script exits 1 even when PAT is absent (grep’s own status). Use if grep -Eq …; then exit 1; fi.
  • uniq without sort only collapses adjacent duplicates. uniq -u keeps lines that appear once; it is not sort -u.
  • sort -k2 is field 2 through end of line; field 2 alone is -k2,2. sort -u with -k uniques 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 — use grep -F or \..
  • sed -i without backup is irreversible. GNU form is -i.bak; BSD/macOS wants -i '' / -i '.bak'.
  • cut (and simple awk -F,) break on quoted CSV commas — use a real CSV tool or Python.
  • Prefer grep -E for regex; grep -F for literals; grep -P is GNU-only and not for portable scripts.

For more details, try man <command> in your terminal.

Previous05 Find & LocateNext07 Processes & Signals