find・grep・awkの組み合わせ方 - 実務で使える活用テクニック
実践編では、find、grep、awk を組み合わせた実用的なデータ処理パターン、業務での活用事例、パフォーマンス最適化を解説する。エンジニア・データアナリスト向けの即戦力スキルを習得する。
組み合わせテクニック
真の Linux マスターは、find、grep、awk を組み合わせて使う。単体では難しい処理も、組み合わせることで強力な解決策になる。
パイプでつなぐ基本パターン
find + grep の組み合わせ:
# ログファイルからERRORを含むファイルを特定
find /var/log -name "*.log" -exec grep -l "ERROR" {} \;
# txtファイル群から"password"を行番号付きで検索
find /home -name "*.txt" | xargs grep -n "password"grep + awk の組み合わせ:
# エラー行から日付・時刻・最後のフィールドを抽出
grep "ERROR" /var/log/app.log | awk '{print $1, $2, $NF}'
# nginxプロセスのCPU使用率を合計
ps aux | grep "nginx" | awk '{sum+=$4} END {print "CPU使用率合計:", sum "%"}'find + awk の組み合わせ:
# ログファイルの総サイズとファイル数を計算
find /var -name "*.log" -printf "%s %p\n" | awk '{size+=$1; count++} END {printf "総サイズ: %.2f MB ファイル数: %d\n", size/1024/1024, count}'実務レベルの複合処理
シナリオ1: Webサーバーのアクセス解析
過去1週間のアクセスログから、エラーが多いIPアドレス TOP10 を抽出する。
find /var/log/apache2 -name "access.log*" -mtime -7 | \
xargs grep " 5[0-9][0-9] " | \
awk '{print $1}' | \
sort | uniq -c | \
sort -rn | \
head -10 | \
awk '{printf "%-15s %d回\n", $2, $1}'ステップ解説:
find: 1週間以内のアクセスログファイルを検索grep: 5xx エラー(サーバーエラー)の行を抽出awk: IPアドレス(1列目)のみを抽出sort | uniq -c: IPアドレス別にカウントsort -rn: カウント数で降順ソートhead -10: TOP10 を取得awk: 見やすい形式で出力
シナリオ2: 古い一時ファイルの一括削除
システム全体から30日以上古い一時ファイルを安全に削除する。
# 1. まず対象ファイルを確認
find /tmp /var/tmp /home -name "*.tmp" -o -name "temp*" -o -name "*.temp" | \
xargs ls -la
# 2. 安全性を確認したら削除実行
find /tmp /var/tmp /home -name "*.tmp" -mtime +30 -size +0 | \
xargs -I {} bash -c 'echo "削除: {}"; rm "{}"'削除前に必ず対象ファイル一覧を表示して確認すること。30日以上古く、サイズが0より大きいファイルのみ対象とする。
シナリオ3: データベース接続ログの分析
MySQL のログから時間帯別の接続数を分析する。
find /var/log/mysql -name "*.log" -mtime -1 | \
xargs grep -h "Connect" | \
awk '{
match($0, /[0-9]{4}-[0-9]{2}-[0-9]{2}T([0-9]{2})/, time_parts);
hour = time_parts[1];
connections[hour]++;
}
END {
print "時間帯別MySQL接続数(過去24時間)";
for (h = 0; h < 24; h++) {
printf "%02d:00-%02d:59 | ", h, h;
count = (h in connections) ? connections[h] : 0;
printf "%5d回 ", count;
for (i = 0; i < count/10; i++) printf "▓";
printf "\n";
}
}'ワンライナーテクニック集
よく使われる便利なワンライナー集。そのまま使えて実用性抜群。
ディスク・ファイル管理:
# 最も大きいファイルTOP20
find . -type f -exec du -h {} + | sort -rh | head -20
# 古いログファイルの総サイズを計算
find /var -name "*.log" -mtime +7 -exec ls -lh {} \; | awk '{size+=$5} END {print "削除可能サイズ:", size/1024/1024 "MB"}'ネットワーク・アクセス解析:
# 今日のアクセス数が多いIPアドレスTOP10
grep "$(date '+%d/%b/%Y')" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# SSHログイン失敗が多いIPアドレス
find /var/log -name "*.log" | xargs grep -h "Failed password" | awk '{print $11}' | sort | uniq -c | sort -rnシステム監視:
# プロセスごとのメモリ使用量
find /proc -maxdepth 2 -name "status" 2>/dev/null | xargs grep -l "VmRSS" | xargs -I {} bash -c 'echo -n "$(basename $(dirname {})): "; grep VmRSS {}'
# 今日のシステムエラー・警告の発生元統計
find /var/log -name "syslog*" | xargs grep "$(date '+%b %d')" | grep -i "error\|warn\|fail" | awk '{print $5}' | sort | uniq -c | sort -rnパイプライン設計パターン
エラーハンドリングと復旧パターン:
本番環境では失敗が前提。エラーを適切に処理し、処理を継続する設計が重要。
#!/bin/bash
set -euo pipefail
handle_error() {
echo "ERROR: パイプライン処理中にエラーが発生しました(行: $1)" >&2
exit 1
}
trap 'handle_error $LINENO' ERR
process_logs_safely() {
local input_pattern="$1"
local output_file="$2"
local temp_dir="/tmp/pipeline_$$"
mkdir -p "$temp_dir"
find /var/log -name "$input_pattern" -type f 2>/dev/null > "$temp_dir/file_list" || {
echo "WARNING: 一部のファイルにアクセスできませんでした" >&2
}
if [[ ! -s "$temp_dir/file_list" ]]; then
echo "ERROR: 処理対象ファイルが見つかりません" >&2
rm -rf "$temp_dir"
return 1
fi
while IFS= read -r logfile; do
if [[ -r "$logfile" ]]; then
grep -h "ERROR\|WARN" "$logfile" 2>/dev/null >> "$temp_dir/errors.log" || true
fi
done < "$temp_dir/file_list"
if [[ -s "$temp_dir/errors.log" ]]; then
awk '
{
if ($0 ~ /ERROR/) error_count++;
if ($0 ~ /WARN/) warn_count++;
}
END {
printf "ERROR: %d件\n", error_count;
printf "WARN: %d件\n", warn_count;
}' "$temp_dir/errors.log" > "$output_file"
fi
rm -rf "$temp_dir"
}並列処理パイプライン:
CPU 集約的処理を並列化して高速化する。
parallel_log_analysis() {
local log_pattern="$1"
local output_dir="$2"
local cpu_cores=$(nproc)
local max_parallel=$((cpu_cores - 1))
find /var/log -name "$log_pattern" -type f | \
xargs -n 1 -P "$max_parallel" -I {} bash -c '
logfile="$1"
output_dir="$2"
result_file="$output_dir/result_$$.tmp"
grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" "$logfile" | \
awk "
{
ip = \$1;
if (match(ip, /^192\.168\./)) region = \"local\";
else if (match(ip, /^10\./)) region = \"internal\";
else region = \"external\";
total_by_region[region]++;
}
END {
for (region in total_by_region) {
printf \"region:%s total:%d\n\", region, total_by_region[region];
}
}" > "$result_file"
' -- {} "$output_dir"
}実務での活用事例
理論より実践。実際の業務でどのように使われているかを職種別に紹介する。
Webエンジニアの場合
急な本番障害対応:
「サイトが重い」との報告。原因を素早く特定する必要がある。
# 1. エラーログの確認
find /var/log/apache2 /var/log/nginx -name "*.log" | xargs grep -E "$(date '+%d/%b/%Y')" | grep -E "5[0-9][0-9]|error|timeout" | tail -50
# 2. 重いクエリの特定
find /var/log/mysql -name "*slow.log" | xargs grep -A 5 "Query_time" | awk '/Query_time: [5-9]/ {getline; print}'
# 3. 異常なアクセスパターンの検出
grep "$(date '+%d/%b/%Y')" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | awk '$1 > 1000 {print "異常アクセス:", $2, "回数:", $1}'手動での確認なら30〜60分かかる作業が、5分で完了する。
月次レポート作成:
先月のアクセス統計とエラー率をまとめる。
#!/bin/bash
LAST_MONTH=$(date -d "last month" '+%b/%Y')
echo "=== $LAST_MONTH アクセス統計レポート ==="
# 総アクセス数
TOTAL_ACCESS=$(find /var/log/apache2 -name "access.log*" | xargs grep "$LAST_MONTH" | wc -l)
echo "総アクセス数: $TOTAL_ACCESS"
# ユニークビジター数
UNIQUE_VISITORS=$(find /var/log/apache2 -name "access.log*" | xargs grep "$LAST_MONTH" | awk '{print $1}' | sort -u | wc -l)
echo "ユニークビジター: $UNIQUE_VISITORS"
# エラー率
ERROR_COUNT=$(find /var/log/apache2 -name "access.log*" | xargs grep "$LAST_MONTH" | grep -E " [45][0-9][0-9] " | wc -l)
ERROR_RATE=$(echo "scale=2; $ERROR_COUNT * 100 / $TOTAL_ACCESS" | bc)
echo "エラー率: $ERROR_RATE%"
# 人気ページTOP10
echo "=== 人気ページ TOP10 ==="
find /var/log/apache2 -name "access.log*" | xargs grep "$LAST_MONTH" | awk '{print $7}' | grep -v "\.css\|\.js\|\.png\|\.jpg" | sort | uniq -c | sort -rn | head -10インフラエンジニアの場合
サーバー監視・メンテナンス:
複数サーバーの健康状態を定期的にチェックする。
#!/bin/bash
echo "=== サーバー健康状態レポート ==="
date
# ディスク使用率警告
echo "=== ディスク使用率 (80%以上で警告) ==="
df -h | awk 'NR>1 {gsub(/%/, "", $5); if($5 > 80) printf "WARNING: %s: %s使用中 (%s%%)\n", $6, $3, $5}'
# メモリ使用率
echo "=== メモリ使用状況 ==="
free -m | awk 'NR==2{printf "メモリ使用率: %.1f%% (%dMB / %dMB)\n", $3*100/$2, $3, $2}'
# 高CPU使用プロセス
echo "=== CPU使用率TOP5 ==="
ps aux --no-headers | sort -rn -k3 | head -5 | awk '{printf "%-10s %5.1f%% %s\n", $1, $3, $11}'
# エラーログの急増チェック
echo "=== 直近1時間のエラー件数 ==="
find /var/log -name "*.log" -mmin -60 | xargs grep -h -E "$(date '+%b %d %H')|$(date -d '1 hour ago' '+%b %d %H')" | grep -ci errorデータアナリストの場合
大量データの前処理:
数GBの CSV ファイルを Excel で開けない。事前に加工が必要。
#!/bin/bash
CSV_FILE="sales_data_2024.csv"
OUTPUT_DIR="processed_data"
mkdir -p $OUTPUT_DIR
# ファイルサイズ・行数確認
echo "ファイルサイズ: $(du -h "$CSV_FILE" | cut -f1)"
echo "総行数: $(wc -l < "$CSV_FILE")"
# データ品質チェック
echo "空行数: $(grep -c '^$' "$CSV_FILE")"
echo "不正な行数: $(awk -F',' 'NF != 5 {count++} END {print count+0}' "$CSV_FILE")"
# 月別データに分割
awk -F',' 'NR==1 {header=$0; next}
{
month=substr($1,1,7);
if(!seen[month]) {
print header > "'$OUTPUT_DIR'/sales_" month ".csv";
seen[month]=1;
}
print $0 > "'$OUTPUT_DIR'/sales_" month ".csv"
}' "$CSV_FILE"
# 各月のサマリー作成
find $OUTPUT_DIR -name "sales_*.csv" | sort | while read file; do
month=$(basename "$file" .csv | cut -d'_' -f2)
total_sales=$(awk -F',' 'NR>1 {sum+=$4} END {print sum}' "$file")
record_count=$(expr $(wc -l < "$file") - 1)
printf "%s: %d件, 売上合計: %d\n" "$month" "$record_count" "$total_sales"
done業界別ケーススタディ
ゲーム開発業界:大規模ログ解析
オンラインゲームで1日100GBのプレイヤー行動ログから、チート検出とゲームバランス分析を行う。
analyze_game_logs() {
local log_date="$1"
local output_dir="/analysis/$(date +%Y%m%d)"
mkdir -p "$output_dir"
find /game/logs -name "*${log_date}*.log" -type f | \
xargs grep -h "PLAYER_ACTION" | \
awk -F'|' '
{
player_id = $3;
action = $4;
value = $5;
# 異常に短時間での大量アクション検出
if (action == "LEVEL_UP") {
player_levelups[player_id]++;
if (player_levelups[player_id] > 10) {
print "SUSPICIOUS_LEVELUP", player_id > "/tmp/cheat_suspects.log";
}
}
# 金額の異常な増加
if (action == "GOLD_CHANGE" && value > 1000000) {
print "SUSPICIOUS_GOLD", player_id, value > "/tmp/gold_anomaly.log";
}
player_actions[player_id]++;
total_actions++;
}
END {
avg_actions = total_actions / length(player_actions);
for (player in player_actions) {
if (player_actions[player] > avg_actions * 5) {
printf "HIGH_ACTIVITY: %s (%d actions)\n", player, player_actions[player];
}
}
}'
}EC・小売業界:顧客行動分析
EC サイトのアクセスログから顧客の購買パターンを分析する。
analyze_customer_journey() {
local analysis_period="$1"
local output_dir="/analytics/customer_journey"
mkdir -p "$output_dir"
find /var/log/nginx -name "access.log*" | \
xargs grep "$analysis_period" | \
awk '
BEGIN { session_timeout = 1800; }
{
ip = $1;
url = $7;
if (url ~ /\/checkout|\/purchase/) {
purchase_sessions[ip]++;
}
if (url ~ /\/products\/([0-9]+)/) {
match(url, /\/products\/([0-9]+)/, product_match);
product_views[product_match[1]]++;
}
}
END {
print "=== 顧客ジャーニー分析 ===";
for (product in product_views) {
printf "商品ID %s: %d回閲覧\n", product, product_views[product];
}
}'
}パフォーマンス最適化
基本的な高速化テクニック
# 1. 検索範囲を限定する find /var/log -name "*.log" # 良い例 # find / -name "*.log" # 悪い例(遅い) # 2. ロケール最適化 LC_ALL=C grep "ERROR" huge.log # 3. 固定文字列の場合は -F grep -F "literal_string" file.txt # 4. 不要なディレクトリをスキップ find /var -path "*/node_modules" -prune -o -name "*.log" -print
上級最適化テクニック
# 並列処理で高速化 find /var/log -name "*.log" | xargs -P 4 grep "ERROR" # 圧縮ファイル直接検索 zgrep "ERROR" /var/log/app.log.gz # memmap でファイルを高速読み込み(GNU grep) grep --mmap "ERROR" huge_file.log
パフォーマンス測定と監視
# 実行時間を測定
time grep "ERROR" /var/log/huge.log
# 詳細なリソース使用量を測定
/usr/bin/time -v grep "ERROR" /var/log/huge.log
# 並列処理の効果測定
for cores in 1 2 4 8; do
echo "並列数: $cores"
time find /var/log -name "*.log" | xargs -P $cores grep -c "ERROR"
done