Linux: The Three Musketeers of Text Processing
If grep is for "finding," then awk, sed, and cut are for "changing." cut slices columns, awk generates data reports, sed does bulk replacements — together with pipes, they can handle nearly any text task.
📋 Prerequisites: You should already know
- Lesson 14: Text Search
1. What You'll Learn
- cut: extract columns by delimiter
- awk: advanced column processing
- sed: replace and delete
- tr: character translation
- diff: file comparison
2. A CSV File Processing Story
(1) The Pain Point: CSV File Needs Format Conversion
Xiaoming received a CSV file and needed to extract columns 2 and 5, remove blank lines, and change the delimiter from commas to tabs.
(2) One Pipeline Chain Does It All
cat data.csv | cut -d',' -f2,5 | sed '/^$/d' | tr ',' '\t' > output.tsv
cut -d',' -f2,5: split by comma, take columns 2 and 5sed '/^$/d': delete blank linestr ',' '\t': replace commas with tabs> output.tsv: output to a new file
(3) The Benefit: Data Processing Without Programming
Xiaoming skipped the hassle of opening Excel or writing a Python script. One command, data processing done.
3. Knowledge Points
(1) cut — Slice by Column
# Split by delimiter
cut -d',' -f1,3 file.csv # Delimiter comma, take columns 1 and 3
cut -d: -f1 /etc/passwd # Delimiter colon, take usernames
> ℹ️ Note: `cut` is suitable for simple column extraction (fixed delimiter, fixed column numbers). For scenarios requiring conditional filtering, calculation, or formatting, use `awk` instead. For example, `awk -F: '$3 >= 1000 {print $1}' /etc/passwd` can filter users with UID ≥ 1000 — `cut` cannot do that.
# Split by character position
cut -c1-5 file.txt # Take characters 1-5
cut -c1,3,5 file.txt # Take characters 1, 3, and 5
(2) awk — Data Report Generator
awk is a complete text processing language. Its core pattern is:
awk 'pattern {action}' file
awk Built-in Variables:
| Variable | Meaning |
|---|---|
$0 |
Entire line |
$1 |
First field |
$2 |
Second field |
NF |
Number of fields |
NR |
Line number |
FS |
Input field separator (default: space/tab) |
OFS |
Output field separator (default: space) |
$0 is the whole line, $1/$2/$3 are fields 1/2/3, NR is the line number, NF is the field count ($NF is the last field). With -F: to specify a delimiter, you can handle most text extraction tasks.
# Basic usage
awk '{print $1}' file.txt # Print column 1
awk '{print $1, $3}' file.txt # Print columns 1 and 3
awk '{print NR, $0}' file.txt # Print line number and entire line
awk '{print $NF}' file.txt # Print last column
awk -F: '{print $1}' /etc/passwd # Specify colon as delimiter
# Pattern matching
awk '/error/ {print}' log.txt # Only process lines containing error
awk '$3 > 100 {print $1, $3}' data.txt # Output only when column 3 > 100
# Aggregation
awk '{sum += $1} END {print sum}' numbers.txt # Sum
awk '{count++} END {print count}' file.txt # Count
(3) sed — Stream Editor
# Replace
sed 's/old/new/' file.txt # Replace first occurrence per line
sed 's/old/new/g' file.txt # Global replace
sed 's/old/new/2' file.txt # Replace only 2nd occurrence per line
sed 's/old/new/g' file.txt > new.txt # Output to new file
sed -i 's/old/new/g' file.txt # In-place edit (modifies file directly)
sed -i.bak 's/old/new/g' file.txt # Backup original as file.txt.bak
> 💡 Tip: `sed -i` modifies the file in place with no undo. Preview the result first without `-i`: `sed 's/old/new/g' file.txt`, then add `-i` once confirmed. Or use `sed -i.bak` to automatically create a backup.
# Delete
sed '/pattern/d' file.txt # Delete lines matching pattern
sed '3d' file.txt # Delete line 3
sed '5,10d' file.txt # Delete lines 5-10
sed '/^$/d' file.txt # Delete blank lines
# Print
sed -n '5,10p' file.txt # Print only lines 5-10
sed -n '/pattern/p' file.txt # Print only matching lines
(4) tr — Character Translation
# Case conversion
echo "hello" | tr 'a-z' 'A-Z' # HELLO
cat file.txt | tr '[:lower:]' '[:upper:]'
# Replace and delete
echo "a,b,c" | tr ',' ' ' # a b c
echo "hello world" | tr -s ' ' # hello world (squeeze repeated spaces)
echo "hello123" | tr -d '0-9' # hello (delete digits)
echo "abc123" | tr -d '[:digit:]' # abc (delete digits)
# Line ending conversion (Windows ↔ Unix)
tr -d '\r' < win.txt > unix.txt # Remove Windows CR
tr '\n' ',' < list.txt # Replace newlines with commas
(5) diff — File Comparison
diff file1.txt file2.txt # Basic comparison
diff -u file1.txt file2.txt # Unified format (more readable)
diff -i file1.txt file2.txt # Ignore case
diff -w file1.txt file2.txt # Ignore whitespace differences
diff -r dir1/ dir2/ # Recursive directory comparison
(6) ▶ Example: cut Column Slicing
# Extract username and shell from /etc/passwd
cut -d: -f1,7 /etc/passwd | head -5
# root:/bin/bash
# daemon:/usr/sbin/nologin
# bin:/usr/sbin/nologin
# Take column 5 (file size) from ls -l output
ls -la | tr -s ' ' | cut -d' ' -f5 | head -5
Output:
TEXTroot:/bin/bash daemon:/usr/sbin/nologin bin:/usr/sbin/nologin sys:/usr/sbin/nologin sync:/usr/sbin/nologin 4096 4096 1234 5678 910
(7) ▶ Example: awk Column Processing
# Print username and home directory
awk -F: '{print $1, "Home:", $6}' /etc/passwd | head -5
# Find regular users with UID greater than 1000
awk -F: '$3 >= 1000 {print $1, "(UID:" $3 ")"}' /etc/passwd
# Calculate total file size
ls -la | grep "^-" | awk '{sum += $5} END {print "Total:", sum/1024/1024, "MB"}'
# Formatted output (printf)
ls -la | awk '{printf "%-20s %8s\n", $9, $5}'
Output:
TEXTroot Home: /root daemon Home: /usr/sbin bin Home: /bin sys Home: /dev nobody Home: /nonexistent alice (UID:1000) bob (UID:1001) Total: 256.5 MB .bashrc 3771 .profile 220 .bash_history 1234
(8) ▶ Example: sed Bulk Replace
# Replace localhost with 127.0.0.1 in the file
sed -i 's/localhost/127.0.0.1/g' config.txt
# Comment out lines starting with listen in the config file
sed -i '/^listen/s/^/#/' nginx.conf
# Insert a new line after line 2
sed '2a\New line inserted' file.txt
# Extract timestamps from log file
sed -n 's/.*\[\(.*\)\].*/\1/p' access.log | head -5
Output:
TEXT07/Jul/2026:10:15:30 +0000 07/Jul/2026:10:15:31 +0000 07/Jul/2026:10:15:32 +0000 07/Jul/2026:10:15:33 +0000 07/Jul/2026:10:15:34 +0000
(9) ▶ Example: tr Character Translation
# Count words in text
cat file.txt | tr -s ' ' '\n' | sort | uniq -c | sort -rn | head -10
# Convert a list to a single comma-separated line
cat list.txt | tr '\n' ',' | sed 's/,$//'
# Remove all comments and blank lines from a config file
grep -v "^#" config.txt | grep -v "^$" | tr -s '\n'
Output:
TEXT42 the 28 and 25 linux 20 file 18 command 15 user 12 system 10 process 8 directory 6 shell apple,banana,cherry,date ServerName localhost Listen 80 DocumentRoot /var/www/html ErrorLog /var/log/apache2/error.log
(10) ▶ Example: diff File Comparison
# Create two versions
echo -e "apple\nbanana\norange" > v1.txt
echo -e "apple\nblueberry\norange" > v2.txt
# Compare
diff -u v1.txt v2.txt
# --- v1.txt
# +++ v2.txt
# @@ -1,3 +1,3 @@
# apple
# -banana
# +blueberry
# orange
(11) ▶ Comprehensive Example: System Monitoring Report
#!/bin/bash
# sys-report.sh - Generate system monitoring report
echo "========================================="
echo " System Monitoring Report"
echo " Generated at: $(date)"
echo "========================================="
echo ""
echo "1. Top 5 Processes by CPU Usage"
ps aux --sort=-%cpu | head -6 | awk '{printf "%-10s %-5s %-5s %s\n", $1, $2, $3, $11}'
echo ""
echo "2. Disk Usage"
df -h | grep "^/" | awk '{printf "%-20s %5s %5s %5s\n", $6, $2, $3, $5}'
echo ""
echo "3. Top 5 by Memory Usage"
ps aux --sort=-%mem | head -6 | awk '{printf "%-10s %-5s %-5s %s\n", $1, $2, $4, $11}'
echo ""
echo "4. Error Count in /var/log"
grep -ric "error" /var/log/*.log 2>/dev/null | grep -v ":0" | head -10
echo ""
echo "5. Listening Ports"
ss -tlnp | tail -n +2 | awk '{print $4, $1}' | sed 's/.*://'
echo ""
echo "========================================="
❓ FAQ
Q: What makes awk more powerful than cut? A: cut can only do simple column slicing (fixed delimiter). awk supports conditional filtering, calculations, and formatted output. awk is a complete programming language — it supports variables, arrays, loops, and functions. Use cut for simple scenarios, awk for complex ones.
Q: Is
sed -irisky for modifying the original file? A: Yes —sed -imodifies the file in place with no undo. Preview without-ifirst, or usesed -i.bakto automatically create a backup. After confirming the result, you can delete the.bakfile.
Q: What's the difference between diff and vimdiff? A: diff is a command-line tool that outputs text comparison results. vimdiff is vim's visual comparison mode with side-by-side split screen and interactive editing. Use diff for quick comparisons, vimdiff for complex ones.
Q: Can tr handle Chinese characters? A: It can, but with caution. Chinese characters are UTF-8 multi-byte encoded, and tr processes by byte, which may split Chinese characters. For Chinese text, use
sedor Python instead.
Q: How to combine multiple text processing commands most efficiently? A: The principle is "one pass, multiple operations." Put simple filtering (grep) first to reduce data volume, then heavier processing later. Avoid creating too many intermediate pipes in
cat file | grep | awk | sed | ....
📖 Summary
cut -d',' -f1,3slices columns by delimiterawk '{print $1}'extracts columns,-F:sets delimiter,/pattern/matches,ENDruns at the endsed 's/old/new/g'global replace,-iin-place edit,/^$/ddelete blank linestr 'a-z' 'A-Z'case conversion,-ddelete,-ssqueeze repeatsdiff -u file1 file2compares file differences
📝 Exercises
- Basic (⭐): Use cut to extract usernames, UIDs (column 3), and login shells from
/etc/passwd, then use tr to convert the content of/etc/hostnameto uppercase - Intermediate (⭐⭐): Use awk to filter files larger than 1MB from
ls -loutput, then use sed to batch replace a port number (8080 → 9090) in a config file, creating a backup of the original - Challenge (⭐⭐⭐): Create two slightly different config files, use
diff -uto view differences, then write a pipeline chain to extract all error lines from a log file, count errors by hour, and output the top 5 peak hours