Linux: テキスト処理三銃士 — awk / sed / cut
grepが「探す」ためのものなら、awk、sed、cutは「変える」ためのものです。cutは列を切り出し、awkはデータレポートを生成し、sedは一括置換を行います — パイプと組み合わせれば、ほぼあらゆるテキストタスクを処理できます。
📋 前提条件:まず以下を完了していること
- レッスン14:テキスト検索
1. このレッスンで学ぶこと
- cut:区切り文字による列抽出
- awk:高度な列処理
- sed:置換と削除
- tr:文字変換
- diff:ファイル比較
2. CSVファイル処理ストーリー
(1) 悩み:CSVファイルのフォーマット変換が必要
小明はCSVファイルを受け取り、2列目と5列目を抽出し、空行を削除し、区切り文字をカンマからタブに変更する必要がありました。
(2) 1パイプラインチェーンで完結
cat data.csv | cut -d',' -f2,5 | sed '/^$/d' | tr ',' '\t' > output.tsv
cut -d',' -f2,5:カンマで分割、2列目と5列目を取得sed '/^$/d':空行を削除tr ',' '\t':カンマをタブに置換> output.tsv:新しいファイルに出力
(3) 得られるもの:プログラミング不要のデータ処理
小明はExcelを開いたりPythonスクリプトを書いたりする手間を省きました。1コマンドでデータ処理完了。
3. 知識ポイント
(1) cut — 列で切り出し
# 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
> ℹ️ 注意:`cut`は単純な列抽出(固定区切り文字、固定列番号)に適しています。条件フィルタリング、計算、フォーマットが必要な場合は`awk`を使用してください。例えば`awk -F: '$3 >= 1000 {print $1}' /etc/passwd`はUID ≥ 1000のユーザーをフィルタリングできます — `cut`では不可能です。
# 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 — データレポートジェネレーター
awkは完全なテキスト処理言語です。コアパターンは:
awk 'pattern {action}' file
awk組み込み変数:
| 変数 | 意味 |
|---|---|
$0 |
行全体 |
$1 |
第1フィールド |
$2 |
第2フィールド |
NF |
フィールド数 |
NR |
行番号 |
FS |
入力フィールド区切り文字(デフォルト:スペース/タブ) |
OFS |
出力フィールド区切り文字(デフォルト:スペース) |
$0は行全体、$1/$2/$3はフィールド1/2/3、NRは行番号、NFはフィールド数($NFは最後のフィールド)。-F:で区切り文字を指定すれば、大部分のテキスト抽出タスクに対応できます。
# 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 — ストリームエディタ
# 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
> 💡 ヒント:`sed -i`は元ファイルをその場で変更し、元に戻せません。まず`-i`なしでプレビュー:`sed 's/old/new/g' file.txt`、確認後に`-i`を追加。または`sed -i.bak`で自動バックアップを作成。
# 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 — 文字変換
# 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 — ファイル比較
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) ▶ サンプル:cutによる列切り出し
# 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
出力:
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) ▶ サンプル:awkによる列処理
# 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}'
出力:
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) ▶ サンプル:sed一括置換
# 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
出力:
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) ▶ サンプル:tr文字変換
# 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'
出力:
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) ▶ サンプル:diffファイル比較
# 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) ▶ 総合例:システム監視レポート
#!/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 "========================================="
❓ よくある質問
Q: awkはcutより何が強力ですか? A: cutは単純な列切り出し(固定区切り文字)しかできません。awkは条件フィルタリング、計算、フォーマット出力をサポートします。awkは完全なプログラミング言語 — 変数、配列、ループ、関数をサポート。単純な場面ではcut、複雑な場面ではawkを使いましょう。
Q:
sed -iで元ファイルを変更するのは危険ですか? A: はい —sed -iは元ファイルをその場で変更し、元に戻せません。まず-iなしでプレビューするか、sed -i.bakで自動バックアップを作成。結果を確認したら.bakファイルを削除できます。
Q: diffとvimdiffの違いは? A: diffはコマンドラインツールでテキスト比較結果を出力。vimdiffはvimの視覚比較モードで、左右分割画面とインタラクティブ編集が可能。簡単な比較にはdiff、複雑な比較にはvimdiffを。
Q: trは中国語文字を処理できますか? A: できますが、注意が必要。中国語文字はUTF-8マルチバイトエンコーディングで、trはバイト単位で処理するため、文字を分割する可能性があります。中国語テキストには
sedやPythonを使用してください。
Q: 複数のテキスト処理コマンドを最も効率よく組み合わせるには? A: 原則は「1パス、複数操作」。単純なフィルタリング(grep)を先に置いてデータ量を減らし、重い処理を後にします。
cat file | grep | awk | sed | ...のように中間パイプを増やしすぎないように。
📖 まとめ
cut -d',' -f1,3で区切り文字により列を切り出しawk '{print $1}'で列を抽出、-F:で区切り文字指定、/pattern/で一致、ENDで終了時実行sed 's/old/new/g'でグローバル置換、-iでその場編集、/^$/dで空行削除tr 'a-z' 'A-Z'で大文字変換、-dで削除、-sで繰り返し圧縮diff -u file1 file2でファイルの差分を比較
📝 練習問題
- 基本(難易度 ⭐):cutで
/etc/passwdからユーザー名、UID(3列目)、ログインシェルを抽出し、trで/etc/hostnameの内容を大文字に変換する - 中級(難易度 ⭐⭐):awkで
ls -l出力から1MB以上のファイルをフィルタリングし、sedで設定ファイルのポート番号(8080 → 9090)を一括置換し、元ファイルのバックアップを作成する - 上級(難易度 ⭐⭐⭐):わずかに異なる2つの設定ファイルを作成し、
diff -uで差分を確認し、パイプラインチェーンでログファイルからエラー行を抽出し、時間別にエラーをカウントし、ピーク時間トップ5を出力する