grep, sed and awk: Linux Text Processing Complete Guide

grep, sed and awk: Linux Text Processing Complete Guide

Tested on: Ubuntu 26.04 LTS · Debian 12 · Arch Linux (GNU grep 3.11, GNU sed 4.9, gawk 5.3) — Last updated: June 2026

grep, sed, and awk are the backbone of Linux text processing. grep finds lines matching a pattern, sed transforms text in-place or in streams, and awk is a full programming language for structured data. Together they handle the vast majority of log analysis, config management, and data extraction tasks directly in the shell — no editor required.

Contents
  1. Prerequisites
  2. grep: Search Files for Patterns
    1. grep with Regular Expressions
  3. sed: Stream Editor
    1. sed Substitution
    2. Advanced sed Operations
  4. awk: Text Processing Language
    1. Patterns, BEGIN/END, and Aggregation
    2. awk as a Programming Language
  5. Regular Expressions Quick Reference
    1. Further Reading

Prerequisites

  • Basic terminal familiarity — you know how to open a shell and run commands
  • GNU coreutils installed (default on all major Linux distros)
  • For grep -P (PCRE), ensure grep was compiled with PCRE support: grep --version | grep -i pcre
  • macOS users: examples use GNU tools. Install via brew install grep gnu-sed gawk and invoke as ggrep, gsed, gawk — or adjust the PATH

grep: Search Files for Patterns

grep (Global Regular Expression Print) reads input line by line and prints every line that matches a pattern. It's the fastest way to filter text — from log files to source code to command output.

# Basic usage
grep "error" /var/log/syslog           # lines containing "error"
grep "error" file1.txt file2.txt       # search multiple files
grep "error" *.log                     # glob expansion across .log files
grep -r "TODO" ~/projects/             # recursive directory search

# Case control
grep -i "error" logfile.txt            # case-insensitive (matches ERROR, Error, error)
grep -v "DEBUG" app.log                # invert: print lines NOT matching

# Counting and file discovery
grep -c "error" logfile.txt            # count matching lines (not occurrences)
grep -l "error" *.log                  # list files that contain the pattern
grep -L "error" *.log                  # list files that do NOT contain it

# Line numbers and context
grep -n "error" logfile.txt            # prefix each match with its line number
grep -A 3 "error" logfile.txt          # 3 lines After the match
grep -B 3 "error" logfile.txt          # 3 lines Before the match
grep -C 3 "error" logfile.txt          # 3 lines of Context (before + after)

# Extract only the matching portion (not the whole line)
grep -o "[0-9]+.[0-9]+" logfile.txt

# Fixed-string mode — no regex, much faster for literal matches
grep -F "192.168.1.1" access.log

# Whole-word match: "test" but not "testing" or "protest"
grep -w "test" file.txt

# Silent mode for scripting: exit 0 if found, 1 if not
grep -q "error" logfile.txt && echo "errors present"

grep with Regular Expressions

grep supports three regex flavors. Basic (BRE) is the default. Extended (ERE) with -E adds +, ?, |, and grouping without backslashes. Perl-compatible (PCRE) with -P adds lookaheads, backreferences, and d / w shortcuts.

# BRE (default) — metacharacters need backslash for grouping/alternation
grep "^error" logfile.txt              # lines starting with "error"
grep "error$" logfile.txt              # lines ending with "error"
grep "err.r" logfile.txt               # dot matches any single character
grep "err*" logfile.txt                # zero or more 'r' after "er"

# ERE with -E
grep -E "error|warning|critical" logfile.txt     # alternation
grep -E "error+" logfile.txt                     # one or more 'r'
grep -E "(WARN|ERROR): .+" logfile.txt           # grouped prefix

# Match IPv4 addresses
grep -E "b([0-9]{1,3}.){3}[0-9]{1,3}b" access.log

# Match email addresses
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}" contacts.txt

# PCRE with -P (most powerful)
grep -P "d{4}-d{2}-d{2}Td{2}:d{2}:d{2}" app.log    # ISO 8601 timestamps
grep -P "(?<=user=)w+" auth.log                            # positive lookbehind
grep -P "^(?!#)" /etc/fstab                                 # lines not starting with #

# Recursive search filtered by file type
grep -r --include="*.py" "import requests" ~/projects/
grep -r --exclude-dir=".git" "password" /etc/

sed: Stream Editor

sed reads input line by line, applies one or more editing commands, and writes the result to stdout. Without -i it never touches the original file — always test a transformation first, then add -i to apply it in place.

# Print specific lines (-n suppresses default output)
sed -n '5p' file.txt                   # print line 5
sed -n '5,10p' file.txt                # print lines 5 through 10
sed -n '/error/p' file.txt             # print matching lines (like grep)

# Delete lines
sed '5d' file.txt                      # delete line 5
sed '5,10d' file.txt                   # delete lines 5-10
sed '/^#/d' file.txt                   # delete comment lines
sed '/^$/d' file.txt                   # delete blank lines
sed '/^[[:space:]]*$/d' file.txt       # delete lines containing only whitespace

sed Substitution

The s command is the workhorse of sed: s/pattern/replacement/flags.

# Replace first occurrence per line
sed 's/foo/bar/' file.txt

# Replace ALL occurrences per line (g = global)
sed 's/foo/bar/g' file.txt

# Case-insensitive replace (GNU sed)
sed 's/foo/bar/gI' file.txt

# In-place editing — modify the actual file
sed -i 's/foo/bar/g' file.txt

# In-place with automatic backup
sed -i.bak 's/foo/bar/g' file.txt      # original saved as file.txt.bak

# Replace only on lines matching a pattern
sed '/^server/s/localhost/127.0.0.1/g' nginx.conf

# Use alternate delimiter (avoid escaping slashes in paths/URLs)
sed 's|http://example.com|https://example.com|g' links.txt
sed 's|/usr/local/bin|/opt/bin|g' script.sh

# Capture groups — backreferences as 1, 2
sed 's/(hello) (world)/2 1/' file.txt         # BRE: "hello world" → "world hello"
sed -E 's/(hello) (world)/2 1/' file.txt           # ERE: same result, cleaner syntax

# Practical cleanup operations
sed 's/[[:space:]]*$//' file.txt        # strip trailing whitespace
sed 's/^[[:space:]]*//' file.txt        # strip leading whitespace
sed 's/^/    /' file.txt                # indent every line with 4 spaces
sed 's/$/ /' file.txt                 # add line continuation marker

Advanced sed Operations

# Insert and append text
sed '/error/i--- ERROR FOUND ---' file.txt       # insert line BEFORE match
sed '/error/a--- REVIEW REQUIRED ---' file.txt   # append line AFTER match

# Replace entire matching line
sed '/^DEPRECATED/c# Line removed — see v2 docs' config.txt

# Chain multiple commands with -e or semicolons
sed -e 's/foo/bar/g' -e '/^#/d' -e '/^$/d' file.txt
sed 's/foo/bar/g; /^#/d; /^$/d' file.txt   # equivalent

# Apply substitution to a specific line range only
sed '10,50s/old/new/g' file.txt

# Range by pattern: apply commands between two markers
sed '/^BEGIN/,/^END/ s/foo/bar/g' file.txt

# Insert contents of another file after a matching line
sed '/^# INSERT_CONFIG_HERE$/r extra.conf' main.conf

# Write matching lines to a separate file (without -n, also prints to stdout)
sed -n '/ERROR/w errors.txt' app.log

# Print line count (alternative to wc -l)
sed -n '$=' file.txt

awk: Text Processing Language

awk processes input record by record (default: one line = one record) and splits each record into fields by a separator (default: whitespace). Its structure is pattern { action } — omit the pattern to run the action on every line, omit the action to print matching lines.

# Field variables
# $0 = entire line, $1 = first field, $2 = second, $NF = last field
awk '{print $1}' file.txt              # first field of every line
awk '{print $1, $3}' file.txt          # first and third, space-separated
awk '{print $1 "-" $3}' file.txt       # custom delimiter between fields
awk '{print $NF}' file.txt             # last field
awk '{print $(NF-1)}' file.txt         # second to last

# Change the field separator with -F
awk -F: '{print $1, $3}' /etc/passwd          # username and UID
awk -F',' '{print $2}' data.csv               # CSV second column
awk -F't' '{print $1, $4}' report.tsv        # TSV columns 1 and 4
awk -F'[,;]' '{print $1}' mixed.txt           # multiple delimiters (regex)

# Set FS in a BEGIN block (same effect as -F)
awk 'BEGIN {FS=":"} {print $1, $7}' /etc/passwd

# Built-in variables
# NR = current record (line) number, NF = number of fields in current record
awk '{print NR, $0}' file.txt          # prefix every line with its number
awk 'NR==5' file.txt                   # print only line 5
awk 'NR>=10 && NR<=20' file.txt        # print lines 10-20
awk 'NF > 3' file.txt                  # only lines with more than 3 fields

Patterns, BEGIN/END, and Aggregation

# Pattern matching
awk '/error/' logfile.txt                        # lines containing "error"
awk '!/error/' logfile.txt                       # lines NOT containing it
awk '$3 > 100' data.txt                          # numeric field comparison
awk '$1 == "WARN" || $1 == "ERROR"' app.log      # field value match

# BEGIN runs once before input, END runs once after
awk 'BEGIN {print "=== Report ==="} {print} END {print "=== Done ==="}' file.txt

# Count matching lines
awk '/error/ {count++} END {print count, "errors found"}' logfile.txt

# Sum a column
awk '{sum += $4} END {printf "Total bytes: %dn", sum}' access.log

# Min/max
awk 'NR==1 {min=max=$1} {if ($1max) max=$1} END {print "Min:", min, "Max:", max}' numbers.txt

# Average with formatted output
awk '{sum += $1; n++} END {printf "Average: %.2fn", sum/n}' values.txt

# Range pattern: print lines between two markers (inclusive)
awk '/^START/,/^END/' file.txt

awk as a Programming Language

# String functions
awk '{print length($0)}' file.txt                          # line length
awk '{print toupper($1), tolower($2)}' file.txt            # case conversion
awk '{print substr($1, 1, 8)}' file.txt                    # substring (1-indexed)
awk '{gsub(/[0-9]+/, "NUM"); print}' file.txt              # global substitution
awk '{n = split($3, parts, ":"); print parts[1]}' file.txt # split field into array

# printf for formatted output
awk '{printf "%-20s %8.2fn", $1, $2}' prices.txt

# Associative arrays — count occurrences per key
awk '{count[$1]++} END {for (k in count) printf "%5d %sn", count[k], k}' logfile.txt

# Two-pass equivalent in one: arrays accumulate, END reports
awk '{bytes[$1] += $10} END {for (ip in bytes) print bytes[ip], ip}' access.log | sort -rn | head -10

# Conditional logic
awk '{
  if ($5 >= 500) status = "CRITICAL"
  else if ($5 >= 400) status = "ERROR"
  else if ($5 >= 300) status = "REDIRECT"
  else status = "OK"
  print $1, $5, status
}' access.log

# Multi-file processing — FNR resets per file, NR does not
awk 'FNR==1 {print "--- File:", FILENAME}' file1.txt file2.txt

Regular Expressions Quick Reference

PatternMatchesWorks in
.Any single characterBRE, ERE, PCRE
*Zero or more of previousBRE, ERE, PCRE
+One or more of previousERE, PCRE
?Zero or one of previousERE, PCRE Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Learn more