Bashスクリプトを使えば、日々のインフラ運用業務を自動化し、作業時間を最大80%削減できます。まずは「#!/bin/bash」から始まるスクリプトの基本構造を理解し、簡単なファイル操作やログ監視を自動化することから始めましょう。この記事では、実務で即座に使える具体的なコード例とベストプラクティスを紹介します。


目次


Bashスクリプトとは何か

Bash(Bourne-Again SHell)スクリプトは、Unix/Linux環境でコマンドを自動実行するためのテキストファイルです。主な特徴は以下の通りです。

特徴説明実務での活用例
自動化繰り返し実行するコマンドを一括処理毎日実行するバックアップ処理
条件分岐実行結果に応じた処理の切り替えサービスの稼働状況に応じた起動制御
変数と関数再利用可能な処理のまとめ共通のログ処理関数を定義
パイプ処理複数コマンドの連携実行ログファイルからエラー抽出

Bashスクリプトの最大のメリットは、GUI操作に比べて圧倒的に高速で再現性が高いことです。例えば、100台のサーバーに対して同じ設定を適用する場合、手動で行うと数時間かかりますが、Bashスクリプトを使えば数分で完了します。

一方で、Bashスクリプトには以下の制限があります。

  • 複雑なデータ構造の扱いが難しい
  • エラー処理が不十分な場合、想定外の動作を引き起こす可能性あり
  • セキュリティリスク(特に権限設定に注意が必要)

これらの特性を理解した上で、適切な用途に活用しましょう。


実行環境のセットアップ

Bashスクリプトを実行するためには、以下の環境が必要です。

1. 対象システムの確認

Bashスクリプトは主にLinux/Unix環境で動作しますが、macOS(Terminal)やWindows(WSL2、Git Bash)でも実行可能です。

環境Bashバージョン確認方法備考
Linux(CentOS/RHEL)bash --versionデフォルトでBashがインストール済み
macOSbash --versionmacOS Catalina以降はzshがデフォルト
Windows(WSL2)bash --versionUbuntu等のLinuxディストリビューションが必要

2. 必要なツールのインストール

基本的なBash環境は標準で提供されていますが、以下のツールを追加でインストールすると作業効率が向上します。

  • シェルチェック(shellcheck):スクリプトの静的解析ツール
    # Ubuntu/Debian
    sudo apt-get install shellcheck
    
    # CentOS/RHEL
    sudo yum install shellcheck
  • jq:JSONデータの処理に便利
    # Ubuntu/Debian
    sudo apt-get install jq
    
    # CentOS/RHEL
    sudo yum install jq
  • tmux:ターミナルマルチプレクサ(長時間実行スクリプトの管理に便利)
    # Ubuntu/Debian
    sudo apt-get install tmux
    
    # CentOS/RHEL
    sudo yum install tmux

3. 実行権限の設定

Bashスクリプトを実行するには、以下の手順で権限を設定します。

  1. スクリプトファイルを作成(例:script.sh
  2. 実行権限を付与
    chmod +x script.sh
  3. 実行
    ./script.sh

注意:スクリプトファイルの先頭行に「shebang(シェバン)」を記述する必要があります。

#!/bin/bash

これにより、実行時にBashが自動的に使用されます。


基本構文と実践的な書き方

Bashスクリプトの基本構文を理解することで、複雑な処理も簡単に記述できるようになります。ここでは、実務で頻繁に使用する構文を中心に解説します。

変数の使い方

変数はデータを一時的に保存するために使用します。Bashでは変数名の前に「$」を付けて参照します。

変数の定義と参照

#!/bin/bash

# 変数定義
NAME="インフラ太郎"
AGE=30
IS_ADMIN=true

# 変数参照
echo "名前: $NAME"
echo "年齢: $AGE"
echo "管理者権限: $IS_ADMIN"

特殊変数

Bashにはシステムが自動的に設定する特殊変数があります。

変数名説明使用例
$0スクリプト名echo "実行中のスクリプト: $0"
$1, $2, ...引数(第1引数、第2引数…)echo "第1引数: $1"
$#引数の総数echo "引数の数: $#"
$?直前のコマンドの終了ステータス(0=成功、非0=失敗)if [ $? -eq 0 ]; then echo "成功"; fi
$$現在のプロセスIDecho "プロセスID: $$"

変数のスコープ

Bashでは変数のスコープ(有効範囲)に注意が必要です。

#!/bin/bash

GLOBAL_VAR="グローバル変数"

function test_scope {
    local LOCAL_VAR="ローカル変数"
    echo "関数内: $GLOBAL_VAR, $LOCAL_VAR"
}

test_scope
echo "関数外: $GLOBAL_VAR"
# echo "関数外: $LOCAL_VAR"  # これはエラー(ローカル変数は外から参照できない)

条件分岐(if文)

条件分岐は、実行結果に応じて処理を切り替えるために使用します。Bashのif文は以下の構文で記述します。

if [ 条件 ]; then
    # 条件が真の場合の処理
elif [ 別の条件 ]; then
    # elifの条件が真の場合の処理
else
    # いずれの条件も真でない場合の処理
fi

主な条件式

条件式説明使用例
-eq等しい(数値)if [ $AGE -eq 30 ]
-ne等しくない(数値)if [ $AGE -ne 25 ]
-ltより小さい(数値)if [ $AGE -lt 40 ]
-gtより大きい(数値)if [ $AGE -gt 20 ]
=等しい(文字列)if [ "$NAME" = "太郎" ]
!=等しくない(文字列)if [ "$NAME" != "花子" ]
-fファイルが存在するif [ -f "/etc/passwd" ]
-dディレクトリが存在するif [ -d "/var/log" ]
-x実行可能ファイルif [ -x "/usr/bin/bash" ]

実践的なif文の例

#!/bin/bash

# 引数の数をチェック
if [ $# -ne 1 ]; then
    echo "エラー: 引数が1つ必要です"
    echo "使用例: $0 <ファイルパス>"
    exit 1
fi

FILE_PATH=$1

# ファイルの存在をチェック
if [ ! -f "$FILE_PATH" ]; then
    echo "エラー: ファイルが存在しません: $FILE_PATH"
    exit 1
fi

# ファイルサイズをチェック
FILE_SIZE=$(stat -c%s "$FILE_PATH")
if [ $FILE_SIZE -gt 1048576 ]; then
    echo "警告: ファイルサイズが1MBを超えています"
fi

# ファイルの読み取り権限をチェック
if [ ! -r "$FILE_PATH" ]; then
    echo "エラー: ファイルを読み取る権限がありません"
    exit 1
fi

echo "ファイルのチェックが完了しました"

ループ処理

ループ処理は、同じ処理を繰り返し実行するために使用します。Bashには主に3種類のループ構文があります。

forループ

#!/bin/bash

# 1. 固定回数のループ
for i in {1..5}; do
    echo "カウント: $i"
done

# 2. コマンドの出力をループ
for file in $(ls *.txt); do
    echo "テキストファイル: $file"
done

# 3. 配列の要素をループ
SERVERS=("web01" "web02" "db01")
for server in "${SERVERS[@]}"; do
    echo "サーバー: $server"
done

whileループ

#!/bin/bash

# 1. 条件付きループ
COUNT=1
while [ $COUNT -le 5 ]; do
    echo "カウント: $COUNT"
    ((COUNT++))
done

# 2. 標準入力を読み取るループ
echo "入力を待っています(Ctrl+Dで終了)"
while read line; do
    echo "入力された内容: $line"
done

untilループ

untilループは、条件が真になるまでループを実行します。

#!/bin/bash

# サービスが起動するまで待機
until systemctl is-active --quiet nginx; do
    echo "Nginxが起動していません。3秒待機..."
    sleep 3
done
echo "Nginxが正常に起動しました"

関数の定義と呼び出し

関数は、再利用可能な処理のまとまりを定義するために使用します。Bashの関数は以下のように定義します。

#!/bin/bash

# 関数定義
function greet {
    local NAME=$1
    echo "こんにちは、$NAMEさん!"
}

# 関数呼び出し
greet "太郎"
greet "花子"

関数には引数を渡すことができ、戻り値としてステータスコード(0=成功、非0=失敗)を返すことができます。

#!/bin/bash

# 関数定義(戻り値付き)
function check_file {
    local FILE_PATH=$1
    if [ -f "$FILE_PATH" ]; then
        return 0  # 成功
    else
        return 1  # 失敗
    fi
}

# 関数呼び出し
check_file "/etc/passwd"
if [ $? -eq 0 ]; then
    echo "ファイルが存在します"
else
    echo "ファイルが存在しません"
fi

関数内で使用する変数にはlocalキーワードを付けることで、ローカル変数として扱うことができます。これにより、他の関数やグローバルスコープとの変数名の衝突を防ぐことができます。


実務で使える具体例

ここでは、実際のインフラ運用業務で役立つBashスクリプトの具体例を紹介します。これらのスクリプトは、そのまま実務で使用することができます。

ファイル操作の自動化

ファイル操作は、Bashスクリプトで最も一般的な用途の1つです。以下のスクリプトは、指定したディレクトリ内の古いログファイルを自動的に削除します。

#!/bin/bash

# 設定
LOG_DIR="/var/log/myapp"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d)

# ログファイルの削除
echo "[$DATE] ログファイルの整理を開始します..."

find "$LOG_DIR" -name "*.log" -type f -mtime +$RETENTION_DAYS -delete

if [ $? -eq 0 ]; then
    echo "[$DATE] 古いログファイルを削除しました"
else
    echo "[$DATE] エラー: ログファイルの削除に失敗しました" >&2
    exit 1
fi

# ディレクトリの圧縮(30日以上前のディレクトリ)
find "$LOG_DIR" -type d -mtime +$RETENTION_DAYS -exec tar -czf {}.tar.gz {} \; -exec rm -rf {} \;

if [ $? -eq 0 ]; then
    echo "[$DATE] 古いログディレクトリを圧縮・削除しました"
else
    echo "[$DATE] エラー: ログディレクトリの圧縮・削除に失敗しました" >&2
    exit 1
fi

echo "[$DATE] ログファイルの整理が完了しました"

このスクリプトのポイント

  • findコマンドで古いファイルを検索し、-deleteオプションで削除
  • -mtime +$RETENTION_DAYSで指定した日数より古いファイルを対象に
  • >&2でエラーメッセージを標準エラー出力に出力
  • exit 1でエラー発生時にスクリプトを終了

ログ監視とアラート通知

サーバーのログ監視は、システムの健全性を維持するために重要です。以下のスクリプトは、Nginxのエラーログを監視し、特定のエラーが発生した場合にメールで通知します。

#!/bin/bash

# 設定
LOG_FILE="/var/log/nginx/error.log"
ALERT_KEYWORD="Connection refused"
ALERT_EMAIL="admin@example.com"
DATE=$(date +"%Y-%m-%d %H:%M:%S")

# ログファイルの存在を確認
if [ ! -f "$LOG_FILE" ]; then
    echo "[$DATE] エラー: ログファイルが存在しません: $LOG_FILE" >&2
    exit 1
fi

# エラー検出
ERROR_COUNT=$(grep -c "$ALERT_KEYWORD" "$LOG_FILE")

if [ $ERROR_COUNT -gt 0 ]; then
    # エラーが発生した場合の処理
    ERROR_LINES=$(grep "$ALERT_KEYWORD" "$LOG_FILE" | tail -n 5)
    SUBJECT="[$DATE] Nginxエラーアラート($ERROR_COUNT件)"
    BODY="以下のエラーが発生しました:\n\n$ERROR_LINES\n\n詳細はログファイルを確認してください。"

    # メール送信(mailコマンドが利用可能な場合)
    echo -e "$BODY" | mail -s "$SUBJECT" "$ALERT_EMAIL"

    echo "[$DATE] アラートを送信しました:$ERROR_COUNT件のエラーを検出"
else
    echo "[$DATE] エラーは検出されませんでした"
fi

このスクリプトのポイント

  • grep -cで特定のキーワードの出現回数をカウント
  • tail -n 5で直近5件のエラーのみ抽出
  • mailコマンドでアラートメールを送信(環境によっては別の方法が必要)
  • ログファイルの存在チェックとエラーハンドリング

注意:メール送信機能は環境によって異なります。以下の方法で代替できます。

  • SMTPサーバーを使用してメール送信
  • SlackやTeamsなどのチャットツールに通知
  • AWS SNSやPagerDutyなどのアラートサービスを使用

システム状態チェック

システムの状態を定期的にチェックすることは、障害の早期発見につながります。以下のスクリプトは、CPU使用率、メモリ使用率、ディスク使用率を監視し、閾値を超えた場合にアラートを発します。

#!/bin/bash

# 設定
CPU_THRESHOLD=90
MEM_THRESHOLD=85
DISK_THRESHOLD=90
ALERT_EMAIL="admin@example.com"
DATE=$(date +"%Y-%m-%d %H:%M:%S")

# CPU使用率のチェック
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
if [ $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc) -eq 1 ]; then
    echo "[$DATE] 警告: CPU使用率が閾値を超えました(${CPU_USAGE}%)"
    ALERT_TRIGGERED=true
fi

# メモリ使用率のチェック
MEM_USAGE=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
if [ $MEM_USAGE -gt $MEM_THRESHOLD ]; then
    echo "[$DATE] 警告: メモリ使用率が閾値を超えました(${MEM_USAGE}%)"
    ALERT_TRIGGERED=true
fi

# ディスク使用率のチェック
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ $DISK_USAGE -gt $DISK_THRESHOLD ]; then
    echo "[$DATE] 警告: ディスク使用率が閾値を超えました(${DISK_USAGE}%)"
    ALERT_TRIGGERED=true
fi

# アラートの送信
if [ "$ALERT_TRIGGERED" = true ]; then
    SUBJECT="[$DATE] システムリソースアラート"
    BODY="CPU使用率: ${CPU_USAGE}%\nメモリ使用率: ${MEM_USAGE}%\nディスク使用率: ${DISK_USAGE}%"

    # メール送信(mailコマンドが利用可能な場合)
    echo -e "$BODY" | mail -s "$SUBJECT" "$ALERT_EMAIL"

    echo "[$DATE] アラートを送信しました"
    exit 1
else
    echo "[$DATE] システムリソースは正常です"
fi

このスクリプトのポイント

  • topコマンドでCPU使用率を取得
  • freeコマンドでメモリ使用率を取得
  • dfコマンドでディスク使用率を取得
  • bcコマンドで浮動小数点の比較を実行
  • 閾値を超えた場合にアラートを発報

注意:このスクリプトを定期的に実行するには、cronジョブに登録することをお勧めします。

# 毎時0分に実行
0 * * * * /path/to/system_check.sh

デバッグとトラブルシューティング

Bashスクリプトのデバッグは、エラーの原因を特定するために重要です。以下の方法で効果的にデバッグを行いましょう。

1. -xオプションで実行内容を表示する

スクリプトを実行する際に-xオプションを付けると、各コマンドの実行内容が表示されます。

bash -x script.sh

これにより、どのコマンドがどのように実行されたかを確認できます。

2. -eオプションでエラー時に即終了する

-eオプションを付けると、コマンドが失敗した際にスクリプトが即座に終了します。これにより、エラーの早期発見が可能になります。

#!/bin/bash
set -e  # エラー発生時に即座に終了

# 以下のコマンドが失敗するとスクリプトが終了
mkdir /new/directory
touch /new/directory/file.txt

3. -uオプションで未定義変数を検出する

-uオプションを付けると、未定義の変数を参照した際にエラーが発生します。

#!/bin/bash
set -u  # 未定義変数を参照するとエラー

echo "$DEFINED_VAR"  # 定義済みの場合はOK
echo "$UNDEFINED_VAR"  # 未定義の場合はエラー

4. trapコマンドでシグナル受信時の処理を定義

trapコマンドを使用すると、特定のシグナル(例:SIGINT、SIGTERM)を受信した際の処理を定義できます。

#!/bin/bash

# 終了シグナルを受信した際の処理
cleanup() {
    echo "スクリプトが終了します。一時ファイルを削除します..."
    rm -f /tmp/tempfile.*
}

trap cleanup EXIT SIGINT SIGTERM

# メイン処理
echo "スクリプトを実行中..."
sleep 10
echo "完了"

5. ロギングの実装

スクリプトの実行履歴をログファイルに記録することで、後から実行内容を確認できます。

#!/bin/bash

LOG_FILE="/var/log/myapp/script.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")

# ログ出力関数
log() {
    echo "[$DATE] $1" | tee -a "$LOG_FILE"
}

log "スクリプトを開始します"

# メイン処理
log "処理を実行中..."
sleep 5
log "処理が完了しました"

6. shellcheckで静的解析する

shellcheckは、Bashスクリプトの静的解析ツールです。以下のように実行します。

shellcheck script.sh

これにより、以下のような問題を検出できます。

  • 未使用の変数
  • 不正な条件式
  • セキュリティ上の問題(例:ファイルパーミッションの不適切な設定)
  • パフォーマンス上の問題

shellcheckは、CI/CDパイプラインに組み込むことで、コード品質の向上に貢献します。


ベストプラクティスと注意点

Bashスクリプトを書く際には、以下のベストプラクティスと注意点を守ることで、信頼性と保守性の高いスクリプトを作成できます。

1. スクリプトの構造化

  • shebangを明記する
    #!/bin/bash
  • 実行可能権限を付与する
    chmod +x script.sh
  • コメントを適切に記述する
    # このスクリプトは毎日深夜に実行され、古いログファイルを削除します
    # 作成者: インフラ太郎
    # 作成日: 2024-01-01
    # バージョン: 1.0
  • 関数を活用する

    共通処理は関数として定義し、再利用性を高めます。

2. エラーハンドリング

  • set -eを使用してエラー発生時に即座に終了
    set -e
  • set -uを使用して未定義変数を検出
    set -u
  • set -o pipefailを使用してパイプ処理のエラーを検出
    set -o pipefail
  • エラーメッセージを標準エラー出力に出力
    echo "エラー: ファイルが見つかりません" >&2

3. セキュリティ

  • ファイルパーミッションに注意する

    スクリプトファイルのパーミッションは必要最小限に設定します。

よくある質問と回答

Bashスクリプトに関する疑問や実務的な悩みについて、初心者から中級者まで幅広く役立つ回答をまとめました。実際の現場で遭遇しやすい質問を中心に解説します。

Q1. Bashスクリプトを書く際の基本的な注意点は何ですか?

A1. Bashスクリプトを書く際は、まず変数の扱いに注意が必要です。変数名は大文字小文字を区別するため、一貫性を持たせることが重要です。また、スペースや特殊文字を含むファイル名を扱う場合は、必ず二重引用符で囲むようにしましょう。実行権限の設定も忘れずに行い、スクリプトの先頭には「#!/bin/bash」のシェバンを記述します。さらに、エラー処理を組み込むことで、予期せぬ動作を防ぐことができます。例えば、コマンドが失敗した際にスクリプトを終了させる「set -e」オプションを活用すると良いでしょう。

Q2. Bashスクリプトでループ処理をする際のポイントは?

A2. ループ処理では、主に「for」ループと「while」ループが使用されます。forループは、リストやファイルの内容を順に処理する際に便利です。例えば、ファイル内の各行を読み込んで処理する場合は「while read」構文が適しています。ループ内でエラーが発生した際の対処として、トラップ処理(trap)を活用することで、中断時のクリーンアップ処理を行うことができます。また、ループのパフォーマンスを向上させるためには、不要な処理をループ内に記述しないように心がけましょう。

Q3. Bashスクリプトでエラーが発生した際のデバッグ方法は?

A3. Bashスクリプトのデバッグには、主に「-x」オプションと「-v」オプションが利用されます。「-x」オプションを使用すると、スクリプトの実行過程が詳細に表示され、どのコマンドが実行されたかを確認できます。また、「-v」オプションでは、スクリプト自体の内容が表示されるため、構文エラーの特定に役立ちます。さらに、デバッグ専用のログ出力関数を作成しておくと、問題箇所を特定しやすくなります。実行時には、環境変数「PS4」を設定することで、デバッグ出力のフォーマットをカスタマイズすることも可能です。

Q4. Bashスクリプトを安全に実行するための方法は?

A4. Bashスクリプトを安全に実行するためには、まず信頼できるソースからスクリプトを入手することが大前提です。実行前にスクリプトの内容を確認し、不要な権限の付与や危険なコマンド(例えば「rm -rf /」など)が含まれていないかをチェックします。また、スクリプト内で外部からの入力を扱う場合は、入力値の検証を徹底し、コマンドインジェクションを防ぐために、変数を二重引用符で囲むようにします。さらに、実行権限を最小限に抑えるために、必要なユーザーやグループにのみ実行権限を与えることも重要です。

まとめと次のステップ

Bashスクリプトは、コマンドライン操作を自動化する強力な手段であり、日々の作業効率を大きく向上させる可能性を秘めています。この記事では、基本的な構文から変数や条件分岐、ループといった制御構造まで、Bashスクリプトの基礎的な要素について解説しました。スクリプトを記述する際には、実行権限の付与やデバッグ方法、さらにはエラー処理の重要性についても触れました。これらの知識を活用することで、単純な繰り返し作業から複雑な処理まで、柔軟に対応できるスクリプトを作成する第一歩を踏み出すことができるでしょう。

今後は、実際の業務や個人的なプロジェクトにBashスクリプトを適用し、実践的な経験を重ねることをおすすめします。スクリプトの再利用性や保守性を高める工夫、例えば関数の活用やモジュール化、コメントの充実などにも取り組んでみてください。また、他のユーザーとスクリプトを共有したり、オープンソースのスクリプトを参考にしたりすることで、より高度な技術やベストプラクティスを学ぶ機会にも恵まれるはずです。Bashスクリプトの世界は奥深く、学び続けることでさらなる可能性が広がっていきます。

【編集・制作ポリシー】
本記事はInfra Academy編集部が各ベンダー公式ドキュメント・エンジニア監修をもとに作成しています。インフラ・クラウド構築は環境により異なります。本番環境への適用前に必ずテストを実施してください。情報の正確性には万全を期していますが、最新情報は各公式ドキュメントをご確認ください。 編集ポリシーはこちら
ABOUT ME
たから
フリーランスIT講師/エンジニア。 ◆経験:IT講師/インフラエンジニア/PM/マネジメント/採用/運用・保守・構築・設計 ◆取得資格:CCNA/CCNP/LPIC-1/AZ-900//サーティファイC言語/情報処理技術者 ◆サイドビジネス:アパレル事業/複数のWEBメディアを運営