nob@lit-forge

Bash / Zsh ワンライナー集

ログから特定時刻を抜き出す、CSV を整形する、複数ファイルを一括リネーム、ファイル差分集計など、日常で 1 行書けば済む Bash / Zsh のワンライナーを実例中心にまとめた早見表。パイプ・xargs・プロセス置換を組み合わせた実戦パターン。

#Bash#Zsh#ワンライナー#shell

ログから情報を抜く

# 直近 1 時間の ERROR 行を色付きでタイムスタンプ順に
grep -a "ERROR" app.log | grep "$(date -u +"%Y-%m-%dT%H" -d '1 hour ago')"

# ログ行を 5xx ステータスでフィルタ、リクエストパスの頻度を集計
awk '$9 ~ /^5/ {print $7}' access.log | sort | uniq -c | sort -rn | head

# nginx ログから User-Agent 上位 10
awk -F\" '{print $6}' access.log | sort | uniq -c | sort -rn | head -10

# 日付範囲で絞る(grep の範囲検索)
awk '/2026-04-18T10:00/,/2026-04-18T11:00/' app.log

ファイル一括操作

# 拡張子を一括変換(jpeg → jpg)
for f in *.jpeg; do mv "$f" "${f%.jpeg}.jpg"; done

# ファイル名に日付 prefix を付ける
for f in *.txt; do mv "$f" "$(date +%Y%m%d)-$f"; done

# 同じディレクトリで .bak バックアップを作成
for f in *.conf; do cp "$f" "$f.bak"; done

# 1MB 未満の画像を一括で最適化(ImageMagick)
find . -name "*.png" -size -1M -exec convert {} -strip -quality 85 {} \;

# 空のディレクトリを削除
find . -type d -empty -delete

# 最終更新が 30 日以上前のファイルを削除
find /tmp -type f -mtime +30 -delete

CSV / TSV / JSON 整形

# CSV の N 列目を抜く(カンマ区切り)
awk -F, '{print $3}' data.csv

# CSV にヘッダ付きで新しい列を追加(row 番号)
awk 'BEGIN{FS=OFS=","} NR==1{print "no", $0; next} {print NR-1, $0}' data.csv

# TSV を JSON 配列に変換(jq 使用)
jq -Rsn '[inputs | split("\n") | .[] | select(length>0) | split("\t")]' < data.tsv

# JSON から特定キーの値だけ抽出
jq '.users[] | select(.active==true) | .email' users.json

# 大きな JSON を整形
jq '.' < big.json | less

# CSV ヘッダを維持して重複行を削除
awk 'NR==1 || !seen[$0]++' data.csv

プロジェクト横断検索

# 大きいファイル上位 10 件
du -ah . | sort -rh | head -10

# node_modules 除外で TODO コメントを探す
grep -rn --exclude-dir=node_modules --include="*.ts" "TODO" .

# ripgrep でより高速に(.gitignore 尊重)
rg -n "TODO" --type ts

# 特定文字列を含むファイルの一覧だけ出す
grep -rl "deprecated" src/

# コミット済みの全ファイルから検索(git grep)
git grep -n "apiKey"

ネットワーク調査

# HTTP レスポンス時間の内訳(DNS / 接続 / TTFB / 総計)
curl -w '@-' -o /dev/null -s https://example.com <<'EOF'
namelookup:  %{time_namelookup}\n
connect:     %{time_connect}\n
starttransfer: %{time_starttransfer}\n
total:       %{time_total}\n
EOF

# あるホストの Let's Encrypt 証明書の残日数
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate

# 開いてる port を一発で列挙
ss -tlnp | awk 'NR>1 {print $4}' | sort -u

# IPv4 アドレスだけ抽出
ip -4 a | awk '/inet / {print $2}'

Git ワンライナー

# 直近のブランチを最近使った順に
git for-each-ref --sort=-committerdate refs/heads/ --format='%(refname:short)' | head

# マージ済ブランチを一括削除
git branch --merged main | grep -v 'main\|\*' | xargs -n1 git branch -d

# 行数の変更が多いファイル TOP 10
git log --format='' --name-only | sort | uniq -c | sort -rn | head

# 特定ファイルをいじった全コミット
git log --follow --oneline -- path/to/file

# 自分のコミット数
git log --author="$(git config user.email)" --oneline | wc -l

日時・データ生成

# ISO 8601(UTC)
date -u +"%Y-%m-%dT%H:%M:%SZ"

# N 分前の時刻
date -d '15 minutes ago' +"%Y-%m-%d %H:%M"

# タイムスタンプ(秒)を ISO 8601 に
date -d @1735689600 -u +"%Y-%m-%dT%H:%M:%SZ"

# ランダム英数字 16 文字
head -c 32 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 16

# UUID v4 を生成
cat /proc/sys/kernel/random/uuid

# 1〜100 のランダム整数
shuf -i 1-100 -n 1

応用: データパイプラインの定型

# アクセスログ → 1 分ごとの RPS を集計 → CSV
awk '{print $4}' access.log | awk -F: '{print $2":"$3}' | sort | uniq -c | awk 'BEGIN{OFS=","}{print $2, $1}'

# ファイル名の衝突チェック(大文字小文字違いの重複検出)
find . -type f | awk '{lower=tolower($0); count[lower]++; paths[lower]=paths[lower] "\n" $0} END {for (k in count) if (count[k]>1) print paths[k]}'

# tar で転送中の進捗を見る
tar -cf - src/ | pv | ssh host 'cat > dst.tar'

# xargs の並列実行(-P 4 で 4 並列)
find . -name "*.png" -print0 | xargs -0 -n1 -P4 pngquant --quality=65-80 --skip-if-larger

Tips

  • 危険な操作は echo で dry-run: for f in *.tmp; do echo rm "$f"; done で意図を確認してから実行
  • 引数に空白・記号が混ざるなら必ず" " で囲む: 変数展開は原則クオート
  • xargs には-0(NUL 区切り): find -print0 | xargs -0 で空白 / 改行入りファイル名にも耐える
  • 長いワンライナーは関数化: ~/.bashrcfunction rg-todo() { rg -n "TODO" "$@"; } を追加すると習慣化しやすい