grep, sed and awk Basics: A Practical Introduction with Examples

Text processing · 8 min read · Updated 25 Oct 2026

On this page
  1. grep: find matching lines
  2. sed: find and replace
  3. awk: work with columns
  4. Putting them together
  5. Quick reference

grep, sed and awk are the three classic text tools on Linux. Each has a clear job:

  • grep finds lines that match a pattern.
  • sed edits text as it flows past, most often find and replace.
  • awk splits lines into fields, so you can pick columns, filter on values and add things up.

You do not need to master any of them to get a lot of value. This guide covers the parts you will actually use, with examples you can run. To follow along, save these lines as access.log:

203.0.113.5 - - [25/Oct/2026:10:01:02 +0000] "GET / HTTP/1.1" 200 5120
198.51.100.7 - - [25/Oct/2026:10:01:05 +0000] "GET /about HTTP/1.1" 200 2048
203.0.113.5 - - [25/Oct/2026:10:02:11 +0000] "GET /missing HTTP/1.1" 404 512
192.0.2.44 - - [25/Oct/2026:10:03:30 +0000] "POST /login HTTP/1.1" 302 0
203.0.113.5 - - [25/Oct/2026:10:04:00 +0000] "GET /about HTTP/1.1" 200 2048

grep: find matching lines

Give grep a pattern and one or more files, and it prints every line that contains the pattern:

$ grep POST access.log
192.0.2.44 - - [25/Oct/2026:10:03:30 +0000] "POST /login HTTP/1.1" 302 0

The options you will reach for most:

OptionMeaning
-iIgnore case: error also matches ERROR
-nShow line numbers
-vInvert: show lines that do not match
-cCount matching lines instead of printing them
-wMatch whole words only: -w cat skips concatenate
-rSearch all files under a directory
-lPrint only the names of files that match
-oPrint only the matching part, not the whole line
-EExtended regular expressions (|, +, ?, {n} without backslashes)
-FTreat the pattern as fixed text, not a regex
-A n, -B n, -C nAlso show n lines after, before, or around each match

Some real examples:

$ grep -c " 404 " access.log                 # how many 404s
1
$ grep -rn "TODO" src/                       # every TODO with file and line number
$ grep -rl "mysql_connect" --include="*.php" .   # which PHP files use it
$ grep -Ev '^(#|$)' /etc/ssh/sshd_config     # config without comments and blank lines
$ grep -E "404|500" access.log               # either status code
$ grep -C 2 "Traceback" app.log              # each error with 2 lines of context

-o with -E is great for extracting things. This pulls every IPv4-looking address from a file:

$ grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' access.log | sort -u
192.0.2.44
198.51.100.7
203.0.113.5

In scripts, grep -q prints nothing and only sets the exit status, which is perfect for if tests:

if grep -q "^alice:" /etc/passwd; then
    echo "alice exists"
fi
Put single quotes around patterns that contain spaces or special characters such as *, $ or |, so the shell passes them to grep unchanged. GNU grep also has -P for Perl-style regexes like \d+, but macOS grep does not.

sed: find and replace

sed reads text line by line, applies your commands, and prints the result. The original file is not changed unless you ask. The command you will use 90% of the time is s for substitute:

$ echo "hello world" | sed 's/world/there/'
hello there

By default s replaces only the first match on each line. Add g to replace all of them:

$ echo "a a a" | sed 's/a/b/'
b a a
$ echo "a a a" | sed 's/a/b/g'
b b b

Useful variations:

$ sed 's/http:/https:/g' links.txt          # replace everywhere, print result
$ sed 's/error/ERROR/gi' app.log            # case-insensitive match (GNU sed)
$ sed 's|/usr/local|/opt|g' paths.txt       # any delimiter works; handy for paths
$ echo "hello world" | sed 's/world/[&]/'   # & is the whole match
hello [world]

Use -E for extended regexes and capture groups, referenced as \1, \2:

$ echo "2026-10-25" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'
25.10.2026

Printing and deleting lines

sed commands can be limited to certain lines, called addresses. Combine them with -n (do not print every line automatically) and p (print) to show part of a file:

$ sed -n '10,20p' file.txt           # lines 10 to 20
$ sed -n '$p' file.txt               # the last line
$ sed -n '/ERROR/p' app.log          # lines matching a pattern, like grep
$ sed '1d' data.csv                  # everything except the first line (header)
$ sed '/^#/d; /^$/d' app.conf        # remove comments and blank lines
$ sed '/START/,/END/d' file.txt      # delete from START through END
$ sed '5s/foo/bar/' file.txt         # substitute only on line 5

Editing files in place

The -i option writes changes back to the file. This is where GNU sed (Linux) and BSD sed (macOS) differ:

$ sed -i 's/foo/bar/g' file.txt        # GNU sed (Linux)
$ sed -i '' 's/foo/bar/g' file.txt     # BSD sed (macOS): empty backup suffix required
$ sed -i.bak 's/foo/bar/g' file.txt    # works on both; keeps file.txt.bak
sed -i overwrites the file with no undo. Run the command without -i first to check the output, or use -i.bak so you keep a backup. Be especially careful combining it with find or wildcards across many files.

awk: work with columns

awk splits each line into fields. By default, fields are separated by runs of spaces or tabs, and they are named $1, $2 and so on. $0 is the whole line. An awk program is a list of pattern { action } pairs, run for every line.

$ awk '{print $1}' access.log                      # first column: the IP address
$ awk '{print $1, $9}' access.log                  # IP and status code
$ awk '$9 == 404 {print $7}' access.log            # paths that returned 404
/missing

Count requests per IP, a task you will meet again and again with log files:

$ awk '{print $1}' access.log | sort | uniq -c | sort -rn
      3 203.0.113.5
      1 198.51.100.7
      1 192.0.2.44

Choosing the field separator

Use -F for files separated by something other than whitespace:

$ awk -F: '{print $1}' /etc/passwd     # usernames
$ awk -F, '{print $2}' data.csv        # second column of a simple CSV
Splitting on commas works for simple CSV files only. If fields can contain quoted commas, use a real CSV tool such as Python's csv module or mlr (Miller).

NR, NF and totals

awk has built-in variables. NR is the current line number, and NF is the number of fields on the line, so $NF is the last field. BEGIN and END blocks run before the first line and after the last.

$ awk 'NR == 3' access.log                   # just line 3
$ awk 'NR > 1' data.csv                      # skip a header line
$ awk '{print $NF}' access.log               # last field: bytes sent
$ awk 'NF > 0' file.txt                      # drop empty lines
$ awk '{bytes += $10} END {print bytes}' access.log
9728

With a file budget.csv containing a header line and then rent,1200, food,450.50 and net,60:

$ awk -F, 'NR > 1 {total += $2} END {printf "Total: %.2f\n", total}' budget.csv
Total: 1710.50
$ awk -F, 'NR > 1 {printf "%-8s %8.2f\n", $1, $2}' budget.csv
rent      1200.00
food       450.50
net         60.00

printf works like in C: %s for a string, %d for an integer, %.2f for two decimals, and a number sets the width (- means left-aligned). Unlike print, it does not add a newline, so include \n.

awk also has associative arrays, which make "group by" counts easy:

$ awk '{count[$9]++} END {for (s in count) print s, count[s]}' access.log
200 3
302 1
404 1

The order of for (s in count) is not guaranteed, so pipe to sort if order matters.

Putting them together

The tools shine in pipelines. Here, grep picks the 200 responses, awk extracts the path, and sort | uniq -c counts them:

$ grep '" 200 ' access.log | awk '{print $7}' | sort | uniq -c | sort -rn
      2 /about
      1 /

The pipes and xargs guide explains how pipelines work, and the one-liner cheatsheet has many more ready-made examples. For full details see the sed(1) and the grep(1) man page.

Quick reference

  • grep -rni "text" dir/: search a directory, any case, with line numbers.
  • grep -v pattern: lines that do not match.
  • grep -oE 'regex': print only the matches.
  • sed 's/old/new/g' file: replace all, print to screen.
  • sed -i.bak 's/old/new/g' file: replace in place with a backup (GNU and macOS).
  • sed -n '10,20p' file: print a range of lines.
  • awk '{print $1}': first column.
  • awk -F, '{print $2}': second comma-separated column.
  • awk '{s += $3} END {print s}': sum a column.