規模が小さいスクリプトならハードコードでも何とかなる。しかし、環境が開発・検証・本番と増え、複数人で管理するようになった瞬間、その設計の甘さが噴き出す。
この記事では、sourceコマンドを使ってシェルスクリプトの設定を外部ファイルに切り出す方法と、環境別(開発・本番)に設定を切り替える実践的な設計を解説する。RHEL 9 / Rocky Linux 9 / Ubuntu 24.04 LTS で動作確認済みのコード例を交えて紹介する。
この記事のポイント
・source コマンドで外部設定ファイルを読み込み、設定とロジックを分離できる
・APP_ENV 変数で development/production を判定し、設定ファイルを自動切り替えできる
・.env 形式に export を付けた設定管理が現場での実務標準
・必須変数チェックと :=(デフォルト値)で、想定外エラーを未然に防げる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ設定をスクリプト本体に直書きしてはいけないのか
シェルスクリプトに設定値を直書きする「ハードコード」が招くリスクは、主に3つある。本番環境への誤操作リスク
開発用スクリプトをコピーして本番で使い回すと、DB接続先やAPIエンドポイントがそのままになっている。「本番のつもりで実行したら開発DBを操作していた」という逆パターンも起きる。設定値がスクリプト本体に埋め込まれていると、この種のミスを防ぐ仕組みが作れない。
認証情報の漏洩リスク
パスワードやAPIキーをスクリプト本体に書いた状態でGitにコミットすると、履歴に永久に残る。.gitignoreで除外できる設定ファイルに切り出すことが、最低限の対策だ。
変更コストの問題
接続先ホスト名が変わるたびにスクリプト本体を開いて編集しなければならない。設定ファイルが分離されていれば、設定ファイルだけ更新すればよく、スクリプト本体に触れるリスクがない。
sourceコマンドで外部設定ファイルを読み込む基本パターン
sourceコマンド(ドットコマンド. とも書く)は、指定したシェルスクリプトを現在のシェルプロセスで読み込む組み込みコマンドだ。読み込んだファイル内で定義した変数は、呼び出し元のスクリプトでそのまま使える。1. 基本的な読み込み方
まず、設定ファイル(config.sh)を用意する。# config.sh — 設定ファイル DB_HOST="192.168.1.100" DB_NAME="myapp_db" DB_USER="dbuser" DB_PASS="P@ssw0rd" APP_LOG_DIR="/var/log/myapp"
#!/bin/bash # main.sh — メインスクリプト # 設定ファイルの読み込み source /etc/myapp/config.sh # または . /etc/myapp/config.sh でも同じ echo "DB接続先: ${DB_HOST}" echo "ログ出力先: ${APP_LOG_DIR}"
.)は等価だ。bash では両方使えるが、sh(POSIXシェル)では source が使えない環境もあるため、移植性を重視する場合は . を使う。2. 相対パスの罠と絶対パス指定の鉄則
source config.sh のように相対パスで指定すると、スクリプトの置き場所ではなくカレントディレクトリから探しにいく。cronや他のスクリプトから呼ばれた場合に「ファイルが見つからない」エラーが発生する典型的な落とし穴だ。設定ファイルのパスは必ず絶対パスで指定するか、スクリプト自身のディレクトリを基準にする。
#!/bin/bash # スクリプト自身のディレクトリを取得 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # スクリプトと同じディレクトリにある設定ファイルを読み込む source "${SCRIPT_DIR}/config.sh"
${BASH_SOURCE[0]} はスクリプト自身のパスを返す変数だ。$0 と異なり、sourceで読み込まれた場合でも正しくファイルパスを返す。環境別(開発・本番)に設定ファイルを切り替える設計
複数の環境を管理する実務では、設定ファイルをひとつにまとめるのではなく、環境ごとに設定ファイルを用意してスクリプトから自動選択する設計が安全だ。1. 環境変数 APP_ENV で切り替える
#!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # APP_ENV が未設定の場合は development を使う APP_ENV="${APP_ENV:-development}" CONFIG_FILE="${SCRIPT_DIR}/config/${APP_ENV}.sh" # 設定ファイルの存在確認 if [ ! -f "${CONFIG_FILE}" ]; then echo "[ERROR] 設定ファイルが見つかりません: ${CONFIG_FILE}" >&2 exit 1 fi source "${CONFIG_FILE}" echo "環境: ${APP_ENV} / DB接続先: ${DB_HOST}"
# 開発環境で実行(デフォルト) bash main.sh # 本番環境で実行 APP_ENV=production bash main.sh # または環境変数をexportしてから実行 export APP_ENV=production bash main.sh
2. 設定ファイルのディレクトリ構成例
myapp/ ├── main.sh └── config/ ├── development.sh # 開発環境用 ├── staging.sh # 検証環境用 └── production.sh # 本番環境用(.gitignoreで除外)
シェルスクリプトを体系的に学びたい方は、シェルスクリプト実践講座(無料)をチェックしてほしい。設定管理・ログ出力・エラーハンドリングなど、現場で使える設計パターンを網羅している。
.envファイル形式を使った設定管理
近年は12-Factor Appの影響で、.env ファイル形式による設定管理が広く使われている。シェルスクリプトでも同じ考え方を取り入れることができる。1. .envファイルの書き方
# .env.production(本番環境用設定) export DB_HOST="db.example.com" export DB_NAME="myapp_production" export DB_USER="app_user" export DB_PASS="P@ssw0rd_prod" export APP_LOG_DIR="/var/log/myapp" export MAIL_FROM="noreply@example.com"
cshシェルを使う環境では
setenv コマンドが対応するが(csh 環境変数設定の解説参照)、現代のサーバー運用では bash/sh が標準のため export 記法で統一してよい。2. 値にスペースを含む場合の注意
# OK: ダブルクォートでスペースを含む値を保護 export APP_NAME="My Application" export MAIL_SUBJECT="Backup completed" # NG: スペースがあると想定外の解釈になる # export APP_NAME=My Application ← シェルエラーになる
設定ファイルのバリデーションとフェイルセーフ設計
設定ファイルを外部化しても、必須の変数が未定義のまま実行されると深刻な問題を引き起こす。設定読み込み後にバリデーション処理を入れることが実務の鉄則だ。1. 必須変数の存在チェック
#!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" APP_ENV="${APP_ENV:-development}" source "${SCRIPT_DIR}/config/${APP_ENV}.sh" # 必須変数チェック関数 check_required_vars() { local missing=0 for var in DB_HOST DB_NAME DB_USER DB_PASS; do if [ -z "${!var}" ]; then echo "[ERROR] 必須変数 ${var} が設定されていません" >&2 missing=1 fi done if [ "${missing}" -eq 1 ]; then exit 1 fi } check_required_vars echo "バリデーション OK — 処理を開始します"
${!var} は変数名を動的に展開するbashの機能だ。var="DB_HOST" のとき、${!var} は ${DB_HOST} と同じ値を返す。2. デフォルト値の設定(:= 演算子)
必須ではないが省略可能な設定値には、フォールバックを設定しておく。# 変数が未設定または空の場合にデフォルト値を返す(変数は変更しない) LOG_LEVEL="${LOG_LEVEL:-INFO}" # 変数が未設定または空の場合にデフォルト値を設定する(変数も更新する) : "${RETRY_COUNT:=3}" : "${TIMEOUT_SEC:=30}" echo "ログレベル: ${LOG_LEVEL}" echo "リトライ回数: ${RETRY_COUNT}" echo "タイムアウト: ${TIMEOUT_SEC}秒"
${変数名:-デフォルト値} は変数を変更せずデフォルト値を返す。${変数名:=デフォルト値} は変数自体も更新する。コロン(:)コマンドはシェルの組み込みで、引数を評価して何もしない。副作用として := による変数初期化に活用できる。3. set -u による未定義変数の検出
スクリプト冒頭にset -u(または set -o nounset)を記述すると、未定義変数を参照した時点でエラー終了する。誤字による変数名ミスを早期に検出できる。#!/bin/bash set -euo pipefail # -e: エラー発生時に即終了 # -u: 未定義変数を参照したらエラー # -o pipefail: パイプライン途中のエラーも捕捉 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/config/${APP_ENV:-development}.sh"
set -euo pipefail は堅牢なシェルスクリプトの定番設定だ。ただし、sourceで読み込む設定ファイル側で :- などのデフォルト値処理を適切に行わないと、未定義変数でスクリプトが止まることがある点は注意が必要だ。「設定ファイルが読み込まれない」よくあるエラーと対処法
1. No such file or directory:パス指定の落とし穴
source実行時に「bash: /path/to/config.sh: No such file or directory」が出る場合、多くはパスの問題だ。# NG: カレントディレクトリから探すため、cronなどで失敗する source config.sh # OK: BASH_SOURCE[0]でスクリプト自身の場所を基準にする SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/config.sh"
2. 変数が空のまま:exportし忘れに気づく方法
設定ファイルをsourceしたはずなのに変数が空になる場合、export忘れが原因の場合がある。sourceで読み込んだ変数でも、子プロセスに渡すにはexportが必要だ。# 実際に変数が読み込まれているか確認する手順 source "${SCRIPT_DIR}/config/${APP_ENV}.sh" # 現在のシェルで定義されているか確認 echo "DB_HOST=${DB_HOST}" # 子プロセスに渡っているか確認(exportがなければ空になる) bash -c 'echo "子プロセス DB_HOST=${DB_HOST}"'
3. set -u 導入後に「unbound variable」が大量発生する場合
既存のスクリプトにset -u を追加すると、これまで暗黙的に空として扱われていた未定義変数が全てエラーになる。段階的に対応するには、まず set -u を有効にしてエラーを洗い出し、各変数に :- でデフォルト値を設定していくのが現実的だ。実践的なディレクトリ構成とベストプラクティス
1. 推奨ディレクトリ構成
myapp/ ├── bin/ │ └── main.sh # メインスクリプト ├── config/ │ ├── common.sh # 全環境共通設定 │ ├── development.sh # 開発環境固有設定 │ ├── staging.sh # 検証環境固有設定 │ └── production.sh # 本番環境固有設定(gitignore対象) ├── lib/ │ └── functions.sh # 共通関数定義 └── .gitignore
2. 共通設定と環境固有設定を分ける
全環境で共通の設定(ログディレクトリ、スクリプト名等)と、環境ごとに異なる設定(DB接続情報、APIキー等)を分離するとさらに管理しやすくなる。#!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CONFIG_DIR="${SCRIPT_DIR}/../config" APP_ENV="${APP_ENV:-development}" # 共通設定を先に読む source "${CONFIG_DIR}/common.sh" # 環境固有設定で上書き(共通設定より後に読む点が重要) source "${CONFIG_DIR}/${APP_ENV}.sh"
3. .gitignoreの設定
# .gitignore config/production.sh config/staging.sh .env .env.* !.env.example
.env.example(または config/production.sh.example)だけgitに含めることで、チームメンバーが設定ファイルのひな形を把握できる。本記事のまとめ
| やりたいこと | 方法 |
|---|---|
| 外部設定ファイルを読み込む | source /path/to/config.sh |
| スクリプト自身のディレクトリ基準でsource | source "${SCRIPT_DIR}/config.sh"(BASH_SOURCE[0]でSCRIPT_DIR取得) |
| 環境別に設定ファイルを切り替える | source "config/${APP_ENV:-development}.sh" |
| 未設定変数にデフォルト値を使う | ${変数名:-デフォルト値} |
| 未定義変数でスクリプトを即終了させる | set -u(または set -euo pipefail) |
また、
set -euo pipefail の導入と必須変数バリデーションを組み合わせることで、設定ミスを早期に検知できるフェイルセーフな設計が完成する。
シェルスクリプト実践講座を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:bats-coreでシェルスクリプトをテストする方法|ユニットテストの書き方とCI自動実行
- 前のページへ:bashのパラメータ展開で文字列処理を完結させる方法|${var%}・${var//}でsed・awkの外部呼び出しを減らす設計
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます