実践LESSON 09

この教材で学ぶこと Bash特有の挙動と、安全な自動化の実践パターンを学びます。

Bash シェルスクリプト実践

対象読者: 基本的なプログラミング経験があり、Bash 特有の罠・イディオム・実践パターンを学びたい人。


1. Bash の「罠」— 他言語経験者が踏みやすい

# ❌ 変数展開の罠:スペースが入ると2引数になる
FILE="my file.txt"
rm $FILE       # → rm my file.txt(2つの引数)
rm "$FILE"     # ✅ クォートで囲む(常にこちら)

# ❌ 比較演算子の違い
if [ $a == $b ]   # 文字列比較([[ ]] 推奨)
if [ $a -eq $b ]  # 数値比較
if [[ $a == $b ]] # ✅ 推奨(Word splitting なし)

# ❌ コマンド失敗を無視する(デフォルト動作)
cp /src /dst
rm /important  # cp が失敗しても rm が実行される

# ✅ エラーで即停止(スクリプト先頭に書く)
set -euo pipefail
# -e: エラーで即終了
# -u: 未定義変数をエラー
# -o pipefail: パイプ途中のエラーも検知

2. スクリプトの基本構造

#!/usr/bin/env bash
set -euo pipefail

# 定数
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly LOG_FILE="/var/log/myapp/deploy.log"

# ログ関数
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
error() { echo "[ERROR] $*" >&2; exit 1; }

# 引数チェック
usage() {
    cat <<EOF
Usage: $0 [OPTIONS] <environment>

Options:
  -r, --region    AWS region (default: ap-northeast-1)
  -d, --dry-run   Dry run mode

Example:
  $0 --region ap-northeast-1 prod
EOF
    exit 1
}

# 引数パース
REGION="ap-northeast-1"
DRY_RUN=false

while [[ $# -gt 0 ]]; do
    case $1 in
        -r|--region) REGION="$2"; shift 2 ;;
        -d|--dry-run) DRY_RUN=true; shift ;;
        -h|--help) usage ;;
        *) ENVIRONMENT="$1"; shift ;;
    esac
done

[[ -z "${ENVIRONMENT:-}" ]] && usage

log "Deploy to $ENVIRONMENT (region: $REGION, dry-run: $DRY_RUN)"

3. よく使うパターン集

ファイル・ディレクトリ操作

# ファイル存在チェック
[[ -f "$FILE" ]] || error "ファイルが存在しない: $FILE"
[[ -d "$DIR" ]] || mkdir -p "$DIR"

# 一時ファイル(終了時に自動削除)
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT

# ファイルの最終N行を監視
tail -f /var/log/nginx/error.log | grep -E "ERROR|WARN"

テキスト処理

# CSV の2列目を合計(awkはインフラ必須)
awk -F',' '{sum += $2} END {print sum}' data.csv

# ログから特定時間帯を抽出
awk '/2026-03-06 15:0[0-5]/' /var/log/app.log

# sed で設定ファイルの値を書き換え
sed -i 's/^MAX_CONNECTIONS=.*/MAX_CONNECTIONS=200/' /etc/app/config

# JSON を整形(jq はインフラで必須)
aws ec2 describe-instances | jq '.Reservations[].Instances[] | {id: .InstanceId, type: .InstanceType}'

# 特定列だけ表示
ps aux | awk '{print $1, $2, $11}' | column -t

プロセス・サービス管理

# PID ファイルを使った二重起動防止
PIDFILE="/var/run/myapp.pid"
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
    error "既に起動中 (PID: $(cat "$PIDFILE"))"
fi
echo $$ > "$PIDFILE"
trap 'rm -f "$PIDFILE"' EXIT

# バックグラウンドジョブの管理
job_ids=()
for host in server1 server2 server3; do
    ssh "$host" "systemctl restart app" &
    job_ids+=($!)
done

# 全ジョブの完了を待つ
for pid in "${job_ids[@]}"; do
    wait "$pid" || error "ジョブ失敗 PID: $pid"
done
log "全サーバーへのデプロイ完了"

4. 実践スクリプト — AWS EC2 ヘルスチェック

#!/usr/bin/env bash
set -euo pipefail

# 全リージョンのEC2インスタンス状態を確認
check_ec2_health() {
    local region="${1:-ap-northeast-1}"
    
    log "Checking EC2 instances in $region..."
    
    # AWS CLI + jq でインスタンス一覧取得
    aws ec2 describe-instances \
        --region "$region" \
        --query 'Reservations[].Instances[].[InstanceId,InstanceType,State.Name,Tags[?Key==`Name`].Value|[0]]' \
        --output text | \
    while IFS=$'\t' read -r id type state name; do
        printf "%-20s %-15s %-15s %s\n" "$id" "$type" "$state" "${name:-N/A}"
    done
}

# ステータスに応じて色付き出力
colorize() {
    while IFS= read -r line; do
        if [[ "$line" == *"running"* ]]; then
            echo -e "\033[32m$line\033[0m"  # 緑
        elif [[ "$line" == *"stopped"* ]]; then
            echo -e "\033[31m$line\033[0m"  # 赤
        else
            echo "$line"
        fi
    done
}

check_ec2_health "ap-northeast-1" | colorize

5. cron・定期実行

# crontab -e で編集
# 書式: 分 時 日 月 曜日 コマンド

# 毎日午前2時にバックアップ
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

# 5分ごとにヘルスチェック
*/5 * * * * /opt/scripts/health_check.sh

# 平日9時にレポート生成
0 9 * * 1-5 /opt/scripts/daily_report.sh

# systemd timer(cron より推奨)も参照: 02_Infrastructure/Linux/

6. デバッグ

# 実行トレース(各コマンドを表示)
set -x
# ... デバッグしたい処理 ...
set +x

# スクリプト全体をトレース
bash -x script.sh

# 変数の内容確認
declare -p MY_ARRAY  # 配列の中身をダンプ

🔗 次のステップ