grep, sed, awk and jq: the four text tools you will use every week
A working reference for the four commands that handle most text on the command line: grep finds lines, sed edits them, awk works with columns and jq does the same for JSON.
Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?
Log files, config files, CSV exports and API responses are all text. Four tools cover nearly everything you need to do with them: grep selects lines, sed transforms lines, awk works with fields and sums, and jq queries and reshapes JSON. Learn ten forms of each and you stop writing throwaway Python for every small job.
grep: find lines
grep 'ERROR' app.log # lines containing ERROR
grep -i 'timeout' app.log # case-insensitive
grep -n 'ERROR' app.log # with line numbers
grep -c 'ERROR' app.log # count matching lines
grep -v 'DEBUG' app.log # lines NOT matching
grep -w 'user' app.log # whole word only
grep -E 'ERROR|WARN' app.log # extended regex, alternation
grep -F '1.2.3.4' app.log # fixed string, dots are literal
grep -o '[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}' app.log # print only the match
grep -r --include='*.py' 'import requests' . # recursive
grep -l 'TODO' *.sh # only file names
grep -B2 -A5 'Traceback' app.log # 2 lines before, 5 after
Quote patterns in single quotes so the shell does not touch them. -E gives you +, ?, | and () without backslashes. For large source trees, ripgrep (rg) is a faster drop-in that skips ignored files by default.
sed: edit a stream of lines
sed 's/http:/https:/' urls.txt # first match per line
sed 's/http:/https:/g' urls.txt # every match
sed -n '10,20p' app.log # print only lines 10 to 20
sed -n '/^ERROR/p' app.log # print only matching lines
sed '/^#/d; /^$/d' config.conf # delete comments and blank lines
sed -E 's/([0-9]+)ms/\1 ms/g' app.log # extended regex with a group
sed 's|/old/path|/new/path|g' file # any delimiter works
sed -i.bak 's/DEBUG=1/DEBUG=0/' app.env # edit in place, keep a .bak copy
Careful: sed -i rewrites the file. GNU sed accepts -i alone; the BSD sed on macOS needs an argument, so write sed -i '' 's/a/b/' file there, or use -i.bak which works on both. Try the command without -i first and look at the output.
awk: columns, sums and small reports
awk reads each line, splits it into fields $1, $2… on whitespace (or on the separator given with -F) and runs your program for every line that matches an optional pattern. $0 is the whole line, NF the number of fields and NR the line number.
awk '{print $1}' access.log # first column
awk -F: '{print $1, $7}' /etc/passwd # user and shell
awk '$9 == 500' access.log # lines where column 9 is 500
awk '$3 > 1000 {print $1, $3}' sizes.txt # filter and project
awk '{sum += $5} END {print sum}' access.log # sum a column
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head
awk 'NR % 2 == 0' file # every second line
awk -F, 'NR > 1 {print $2}' data.csv # skip the header
awk '{printf "%-20s %8.2f\n", $1, $2}' prices.txt # formatted output
awk 'length($0) > 120' *.py # long lines
For real CSV with quoted commas, awk’s -F, is not enough; use a CSV-aware tool such as Python’s csv module, mlr (Miller) or DuckDB.
jq: the same idea for JSON
jq (jqlang/jq) reads JSON and applies a filter. . is the whole document, .key selects a field, .[] iterates an array and | chains filters, just like a shell pipe.
curl -s https://api.example.com/users | jq . # pretty-print
jq '.name' user.json # one field
jq -r '.name' user.json # raw string, no quotes
jq '.users[].email' data.json # one field from each item
jq '.users[] | select(.active == true) | .name' data.json # filter items
jq '.users | length' data.json # count
jq '.users | map(.age) | add / length' data.json # average
jq -r '.users[] | [.name, .email] | @csv' data.json # to CSV
jq -r '.users[] | "\(.name)\t\(.email)"' data.json # to TSV with string interpolation
jq '{total: .count, first: .items[0].id}' data.json # build a new object
jq -c '.items[]' data.json # one compact object per line
jq -S . config.json # sort keys, good for diffs
jq --arg u alice '.users[] | select(.name == $u)' data.json # pass a shell variable in
jq -s 'add' part1.json part2.json # merge files (slurp)
jq 'keys' config.json # list top-level keys
jq '.. | .id? // empty' data.json # every id at any depth
-rprints strings without quotes, which is what you want when feeding results to other commands.-emakes the exit code non-zero when the result isnullorfalse, useful in scripts.--arg name valuepasses a string and--argjson name valuepasses a number or object; never splice shell variables into the filter text.-Ssorts keys;-cprints compact;--taband--indent ncontrol pretty-printing.
Combining them
# Top 10 client IPs behind 5xx responses
awk '$9 ~ /^5/ {print $1}' access.log | sort | uniq -c | sort -rn | head
# Which pods restart the most
kubectl get pods -A -o json | jq -r '.items[] | "\(.status.containerStatuses[0].restartCount)\t\(.metadata.namespace)/\(.metadata.name)"' | sort -rn | head
# Replace a value in every YAML file that mentions it, keeping backups
grep -l 'replicas: 1' *.yaml | xargs sed -i.bak 's/replicas: 1/replicas: 2/'
Official documentation
Did this work for you?
No votes yet. Be the first.
If it did not work, the report form below tells the editors what went wrong.
See also
- 0049 Pipes and redirection: connecting commands A pipe sends one command's output into another command, and redirection sends output to a file. Together they let small tools build… Beginner · Explains an idea · Linux, macOS Article Basics · Text processing 15 Oct 2026
- 0052 Slice and summarize text with grep, sort, uniq, cut, awk and sed A handful of classic tools can filter, count, extract and rewrite text right in the terminal. This guide shows the patterns you… Intermediate · Step by step · Linux, macOS Article Finding files & text · Text processing 15 Oct 2026
- 0060 Find the biggest files and folders eating your disk A short bash script that lists the largest files over a size limit and the largest subfolders, using find, du, sort and… Beginner · Step by step · Linux, macOS Script Finding files & text · Text processing 15 Oct 2026
- 0076 jq recipes: 23 one-liners for JSON on the command line A runnable collection of jq filters for everyday JSON work: pick fields, filter with select, reshape, sort, count, group, pass shell variables,… Intermediate · Cheat sheet · Linux, macOS, Windows Script Command-line tools · jq & JSON on the command line 15 Oct 2026
- 0422 Dotfiles: keep your shell setup in git Put .bashrc, .zshrc, .gitconfig and your editor settings into a git repository so a new machine is set up in minutes: a… Intermediate · Step by step · Linux, macOS Article Modern command-line tools · Setup 15 Oct 2026
- 0425 jq cheat script: pretty-print, filter and flatten JSON from a file or stdin A small read-only bash wrapper around jq with the recipes you keep looking up: pretty, compact, sorted keys, list paths, pick fields,… Beginner · Cheat sheet · Linux, macOS Script jq & JSON on the command line · Modern command-line tools · Text processing 15 Oct 2026
Repos in the wild
- jq: command-line JSON processor jqlang/jq jq is a small command-line tool for reading, filtering and transforming JSON, like sed and awk for structured data. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Dominik +6,259 chars