実践Bashスクリプト:ゼロから始めるLinux自動化

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Bashスクリプト:コマンド入力だけでは不十分な理由

3年前、私は10台の本番環境VPSのバックアップコマンドを手動で入力するために、一晩中起きていたことがあります。当時は history を使ってコピー&ペーストするだけでした。悲劇は、rm -rf / コマンドの後に誤ってスペースを入れてしまった時に起こりました。幸いすぐに気づいて止められましたが、その一件で痛感しました。Linuxの世界でステップアップしたいなら、自動化は必須であると。

自動化にはどのツールを選ぶべきか?

同じ作業を3回以上繰り返すなら、手作業はやめるべきです。通常、いくつかの選択肢があります:

  • 手動コマンド入力: 一度限りのタスクには速いですが、20台のサーバーで同時に実行する必要がある場合は「悪夢」となります。
  • Alias(エイリアス)の使用: alias ll='ls -la' のような短いコマンドには最適です。しかし、Aliasでは複雑なロジックや if-else 条件は処理できません。
  • Bashスクリプト: 最もバランスの良い選択肢です。PythonやNode.jsを追加インストールすることなく、あらゆるLinuxディストリウルーションで直接動作します。
  • Ansible/Terraform: 大規模なインフラには非常に強力です。しかし、単にディスク容量を素早くチェックしたいだけなら、これらは大げさすぎます。

Bashを使うべき理由(と使わないほうがいい理由)

メリット:

  • Ubuntu、CentOS、超軽量のAlpineまで、100%標準搭載されている。
  • ファイルやシステムプロセスと、ほぼゼロ遅延でやり取りできる。
  • 遭遇するあらゆるエラーの解決策は、10年前からStackOverflowに存在している。

デメリット:

  • Bashの構文には、空白に関する「罠」が多い。
  • デバッグが、高水準言語よりもストレスフルな場合がある。
  • 関数化を怠ると、すぐにスパゲッティコードになりやすい。

本番環境で使える「手堅い」スクリプト構築4ステップ

スクリプトが暴走して誤ってデータを削除してしまう事態を避けるため、私は常に以下の4つのステップを適用しています。

1. 常にシバン(Shebang)と変数を使用する

ファイルの先頭にある #! /bin/bash 行は必須です。これにより、システムはどのインタプリタを使用すべきかを正確に把握できます。また、値を直接書き込む(ハードコーディング)のではなく、明確な変数名を使用しましょう。

#!/bin/bash

# 管理しやすくするため、環境変数は常に大文字で記述する
BACKUP_DIR="/var/backups/nginx_logs"
CURRENT_USER=$(whoami)

echo "こんにちは $CURRENT_USER さん。$BACKUP_DIR へのバックアップを開始します。"

2. 条件分岐の制御 (Conditionals)

Bashでは、二重ブラケット [[ ]] の方が単一の [ ] よりも柔軟です。鉄則:ブラケットの前後には必ず空白を入れてください。これがないと、スクリプトは即座に動作を停止します。

if [[ ! -d "$BACKUP_DIR" ]]; then
    echo "ディレクトリが存在しません。作成中..."
    mkdir -p "$BACKUP_DIR"
else
    echo "ディレクトリは既に存在します。処理を続行します。"
fi

3. ループによる最適化 (Loops)

例えば、ディスク容量の80%を解放するために、50個の古いログファイルを圧縮する必要があるとします。for ループを使えば、2秒足らずで解決できます。

# カレントディレクトリ内のすべての .log ファイルを圧縮する
for log_file in *.log; do
    echo "圧縮中: $log_file"
    tar -czf "${log_file}.tar.gz" "$log_file"
done

4. コードを関数にまとめる (Functions)

ステータス通知のために echo を何度も書いていることに気づいたら、専用のログ関数を作成しましょう。これにより、コードが整理され、メンテナンスが格段に楽になります。

notify() {
    local status=$1
    local msg=$2
    echo "[$(date +'%H:%M:%S')] [$status] $msg"
}

notify "INFO" "スクリプトを起動しています..."
notify "ERROR" "データベースが応答しません!"

本番環境で「生き残る」ためのエラー処理テクニック

優れたスクリプトと初心者のスクリプトの違いは、失敗の処理方法にあります。バックアップスクリプトの小さなミスが、エラーチェックを怠ったために全データを失う原因になるかもしれません。

強力な3点セット:set -euo pipefail

私は常にシバンの後にこの行を追加しています。これはあらゆるスクリプトにとっての「保険」です:

  • -e: コマンドがエラーになった時点でスクリプトを即座に停止する。
  • -u: 未定義の変数を参照した場合にエラーを表示する。
  • -o pipefail: パイプ | の途中で発生したエラーもキャッチする。

終了ステータス ($?) のチェック

rsyncmysqldump のような重要なコマンドの後は、変数 $? を確認しましょう。値が 0 であれば成功、それ以外は何らかの問題が発生したことを意味します。

rsync -avz /src/ /dest/
if [[ $? -ne 0 ]]; then
    echo "同期エラー!ネットワーク接続を確認してください。"
    exit 1
fi

実戦経験からのまとめ

Bashを書くこと自体は難しくありません。難しいのは「いかに安全に書くか」です。長年の運用の末、私は一つの原則にたどり着きました:必ずステージング環境でスクリプトをテストすること。 自分が入力したものを決して100%信じてはいけません。

もしスクリプトが500行を超えるようなら、Pythonへの移行を検討すべき時です。しかし、日常的なシステム管理タスクにおいて、Bashは今でも「王様」です。小さなスクリプトから始めてみてください。作業効率が劇的に向上するのを実感できるはずです。

Share: