Lesson 10: Text Processing
Goal: Learn to transform, filter, and manipulate text data using powerful command-line tools.
Table of Contents
- sort — Sort Lines
- uniq — Remove Duplicates
- cut — Extract Columns
- tr — Translate Characters
- sed — Stream Editor
- awk — Pattern Processing
- Combining Tools
- Exercises
sort — Sort Lines
Basic sort (alphabetical)
echo -e "banana\napple\ncherry\ndate" | sort
Output:
apple
banana
cherry
date
Numeric sort
echo -e "10\n2\n33\n1\n5" | sort -n
Output:
1
2
5
10
33
Without -n, "10" would come before "2" (alphabetical: "1" < "2").
Reverse sort
sort -r file.txt # reverse alphabetical
sort -rn file.txt # reverse numeric (largest first)
Sort by column
# Sort /etc/passwd by UID (3rd field, numeric)
sort -t: -k3 -n /etc/passwd
# Sort by file size (5th column in ls -l)
ls -l | sort -k5 -n
# Sort CSV by 2nd column
sort -t, -k2 data.csv
Human-readable sort
# Sort sizes like 1K, 5M, 2G correctly
du -h /var/log/* 2>/dev/null | sort -h
Sort and remove duplicates
sort -u file.txt
uniq — Remove Duplicates
uniq removes consecutive duplicate lines. Always sort first.
Basic usage
# Remove consecutive duplicates
sort names.txt | uniq
# Count occurrences
sort names.txt | uniq -c
# Show only duplicates
sort names.txt | uniq -d
# Show only unique lines (no duplicates)
sort names.txt | uniq -u
Practical example
# Create test data
cat > access.log << EOF
192.168.1.1
192.168.1.5
192.168.1.1
192.168.1.3
192.168.1.1
192.168.1.5
192.168.1.3
EOF
# Count visits per IP, sorted by frequency
sort access.log | uniq -c | sort -rn
Output:
3 192.168.1.1
2 192.168.1.3
2 192.168.1.5
cut — Extract Columns
cut extracts specific fields or character positions from each line.
Cut by delimiter and field
# Extract usernames from /etc/passwd (field 1, delimiter :)
cut -d: -f1 /etc/passwd
# Extract username and shell (fields 1 and 7)
cut -d: -f1,7 /etc/passwd
# Extract fields 1 through 3
cut -d: -f1-3 /etc/passwd
Cut by character position
# First 10 characters of each line
cut -c1-10 file.txt
# Characters 5 through 15
cut -c5-15 file.txt
# From character 20 to end of line
cut -c20- file.txt
CSV example
cat > employees.csv << EOF
Name,Department,Salary
Alice,Engineering,85000
Bob,Marketing,72000
Carol,Engineering,90000
Dave,Sales,65000
EOF
# Extract names (column 1)
cut -d, -f1 employees.csv
# Extract names and salaries
cut -d, -f1,3 employees.csv
tr — Translate Characters
tr translates (replaces) or deletes characters. It reads from stdin only.
Replace characters
# Convert lowercase to uppercase
echo "hello world" | tr 'a-z' 'A-Z'
# Output: HELLO WORLD
# Convert uppercase to lowercase
echo "HELLO WORLD" | tr 'A-Z' 'a-z'
# Output: hello world
# Replace spaces with underscores
echo "my file name.txt" | tr ' ' '_'
# Output: my_file_name.txt
# Replace colons with tabs
cat /etc/passwd | tr ':' '\t' | head -3
Delete characters
# Delete all digits
echo "abc123def456" | tr -d '0-9'
# Output: abcdef
# Delete all whitespace
echo "hello world" | tr -d ' '
# Output: helloworld
# Delete newlines (join lines)
cat file.txt | tr -d '\n'
Squeeze repeated characters
# Squeeze multiple spaces into one
echo "too many spaces" | tr -s ' '
# Output: too many spaces
# Squeeze blank lines
cat file.txt | tr -s '\n'
sed — Stream Editor
sed is a powerful tool for finding and replacing text in streams or files.
Basic replacement
# Replace first occurrence per line
echo "hello world hello" | sed 's/hello/hi/'
# Output: hi world hello
# Replace ALL occurrences per line (global)
echo "hello world hello" | sed 's/hello/hi/g'
# Output: hi world hi
Replace in files
# Preview changes (print to stdout)
sed 's/old/new/g' config.txt
# Edit file in place
sed -i 's/old/new/g' config.txt
# Create a backup before editing
sed -i.bak 's/old/new/g' config.txt
Delete lines
# Delete line 3
sed '3d' file.txt
# Delete lines 2-5
sed '2,5d' file.txt
# Delete blank lines
sed '/^$/d' file.txt
# Delete comment lines
sed '/^#/d' config.txt
# Delete comments AND blank lines
sed '/^#/d; /^$/d' config.txt
Print specific lines
# Print only line 5
sed -n '5p' file.txt
# Print lines 10-20
sed -n '10,20p' file.txt
# Print lines matching a pattern
sed -n '/error/p' logfile.txt
Advanced sed
# Insert a line before line 3
sed '3i\New line inserted here' file.txt
# Append a line after line 5
sed '5a\Appended after line 5' file.txt
# Replace only on lines matching a pattern
sed '/server/s/80/8080/g' config.txt
# Multiple replacements
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt
Practical examples
# Remove trailing whitespace
sed 's/[[:space:]]*$//' file.txt
# Add prefix to every line
sed 's/^/>>> /' file.txt
# Extract domain from URLs
echo "https://example.com/path" | sed 's|https://\([^/]*\).*|\1|'
# Output: example.com
# Comment out a line in a config
sed -i 's/^PermitRootLogin yes/# PermitRootLogin yes/' /etc/ssh/sshd_config
awk — Pattern Processing
awk is a programming language for text processing. It processes files line by line, splitting each into fields.
Basic usage
By default, awk splits on whitespace. Fields are $1, $2, etc. $0 is the whole line.
# Print the first column
echo "Alice 25 Engineer" | awk '{print $1}'
# Output: Alice
# Print columns 1 and 3
echo "Alice 25 Engineer" | awk '{print $1, $3}'
# Output: Alice Engineer
# Print with custom separator
echo "Alice 25 Engineer" | awk '{print $1 " is an " $3}'
# Output: Alice is an Engineer
Custom field separator
# Parse /etc/passwd (colon-separated)
awk -F: '{print $1, $3}' /etc/passwd
# Parse CSV
awk -F, '{print $1, $3}' employees.csv
Conditions
# Print lines where column 3 > 80000
awk -F, '$3 > 80000 {print $1, $3}' employees.csv
# Print lines matching a pattern
awk '/error/ {print}' logfile.txt
# Print lines longer than 80 characters
awk 'length > 80' file.txt
# Print line numbers
awk '{print NR, $0}' file.txt
Built-in variables
| Variable | Meaning |
|---|---|
$0 |
Entire line |
$1, $2... |
Fields |
NR |
Line number (record number) |
NF |
Number of fields on current line |
FS |
Field separator |
OFS |
Output field separator |
Calculations
# Sum values in column 3
awk -F, 'NR>1 {sum += $3} END {print "Total:", sum}' employees.csv
# Output: Total: 312000
# Average
awk -F, 'NR>1 {sum += $3; count++} END {print "Average:", sum/count}' employees.csv
# Output: Average: 78000
# Min/Max
awk -F, 'NR>1 {if($3>max) max=$3; if(min=="" || $3<min) min=$3} END {print "Min:", min, "Max:", max}' employees.csv
Formatting output
# Printf for formatted output
awk -F, 'NR>1 {printf "%-10s %s\n", $1, $3}' employees.csv
Output:
Alice 85000
Bob 72000
Carol 90000
Dave 65000
Combining Tools
The real power of these tools emerges when you pipe them together.
# Top 10 most common words in a file
cat file.txt | tr ' ' '\n' | tr 'A-Z' 'a-z' | sort | uniq -c | sort -rn | head -10
# Active SSH config (no comments, no blanks)
cat /etc/ssh/sshd_config | sed '/^#/d; /^$/d'
# Disk usage by directory, sorted
du -h --max-depth=1 /home 2>/dev/null | sort -h
# Extract and count HTTP status codes from access log
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# Find users with bash shell, sorted
grep "/bin/bash" /etc/passwd | cut -d: -f1 | sort
Exercises
Exercise 1: sort and uniq
# 1. Create test data
cat > words.txt << EOF
banana
apple
cherry
apple
banana
date
cherry
apple
EOF
# 2. Sort alphabetically
sort words.txt
# 3. Sort and remove duplicates
sort -u words.txt
# 4. Count occurrences of each word
sort words.txt | uniq -c | sort -rn
# 5. Clean up
rm words.txt
Exercise 2: cut and tr
# 1. Extract just usernames from /etc/passwd
cut -d: -f1 /etc/passwd | head
# 2. Convert a string to uppercase
echo "hello world" | tr 'a-z' 'A-Z'
# 3. Replace spaces with newlines
echo "one two three four" | tr ' ' '\n'
Exercise 3: sed and awk
# 1. Create a data file
cat > data.txt << EOF
Server: web01 Status: active Load: 0.5
Server: web02 Status: down Load: 0.0
Server: db01 Status: active Load: 1.2
Server: cache01 Status: active Load: 0.8
EOF
# 2. Extract server names with awk
awk '{print $2}' data.txt
# 3. Find servers that are down
awk '/down/ {print $2}' data.txt
# 4. Replace "down" with "OFFLINE" using sed
sed 's/down/OFFLINE/g' data.txt
# 5. Clean up
rm data.txt
Key Takeaways
sort— sort lines (-nnumeric,-rreverse,-kby column)uniq— remove duplicates (always sort first,-cto count)cut— extract columns (-ddelimiter,-ffield)tr— translate/delete characters (works on stdin only)sed— find & replace in streams (s/old/new/g,-ifor in-place)awk— column-based processing ($1,$2, conditions, math)- Pipe them together for powerful data transformations
Next Lesson: Lesson 11: System Information →