シェルスクリプトで信頼性を高める第一歩は、preflight(プリフライト)チェック の設計です。必要なコマンド・ファイル・権限・ディスク容量が揃っているかをスクリプトの冒頭で確認し、条件が不足していれば処理を始める前に明確なエラーメッセージを出して終了する。
この記事では、コマンドの存在確認・ファイルや権限の検証・ディスク空き容量のチェックを再利用可能な関数にまとめる設計パターンを解説します。実際のバックアップスクリプトへの組み込み例も示します。
動作確認環境: Rocky Linux 9.4 / Ubuntu 24.04 LTS(bash 5.2)
この記事のポイント
・command -v でコマンドの存在を確認し、なければ即 exit する
・ファイル・ディレクトリ・権限は -f/-d/-r/-w テストで事前検証する
・ディスク空き容量は df -k と算術展開で数値化して閾値比較する
・require_cmd / require_dir 関数に集約するとどのスクリプトにも使い回せる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ前提条件チェックが必要なのか
スクリプトが途中で失敗すると、2つの問題が起きます。1つ目は「中途半端な状態が残る」こと。バックアップスクリプトが途中で落ちると、不完全なアーカイブが出力先に残ります。世代管理している場合は「壊れたバックアップが正常ファイルとして記録される」事態になります。
2つ目は「エラーメッセージが不親切」なこと。コマンドが見つからない場合のデフォルトエラーは
rsync: command not found ですが、インストール方法まで伝えてはくれません。スクリプトが自分でチェックして「必要なコマンドがありません: rsync。yum install rsync でインストールしてください。」と出力すれば、対処が速くなります。preflight チェックは「守りの設計」です。メイン処理を書く前に動作の前提条件を明文化する作業でもあります。
コマンドの存在確認(command -v)
コマンドが存在するかどうかを確認するにはcommand -v を使います。which はシェルの組み込みコマンドを検出できない場合があり、エイリアスも見落とすことがあるため、command -v のほうが信頼性が高いです。1. 1コマンドをチェックする基本形
#!/bin/bash set -euo pipefail # コマンドが存在しなければエラーを出して終了する関数 require_cmd() { local cmd="$1" local hint="${2:-}" # インストール方法のヒント(省略可) if ! command -v "${cmd}" &>/dev/null; then echo "[ERROR] 必要なコマンドが見つかりません: ${cmd}" >&2 if [[ -n "${hint}" ]]; then echo " インストール方法: ${hint}" >&2 fi exit 1 fi }
# バックアップスクリプトの先頭で使う例 require_cmd rsync "yum install rsync または apt install rsync" require_cmd tar require_cmd gzip
>&2 でエラーメッセージを標準エラー出力に書くのがポイントです。cronで実行する場合、標準エラー出力だけをメールで受け取る設定にできるため、問題の通知が確実になります。2. 複数コマンドをまとめて確認する
必要なコマンドが複数ある場合は、配列でまとめて確認すると抜け漏れが防げます。check_required_cmds() { local cmds=("$@") local missing=0 for cmd in "${cmds[@]}"; do if ! command -v "${cmd}" &>/dev/null; then echo "[ERROR] コマンドが見つかりません: ${cmd}" >&2 missing=1 fi done if [[ "${missing}" -eq 1 ]]; then echo "[ERROR] 必要なコマンドをインストールしてから再実行してください。" >&2 exit 1 fi } # 使い方 check_required_cmds rsync tar gzip openssl
ファイル・ディレクトリ・権限の検証
コマンドが揃っていても、処理対象のファイルがなかったり書き込み権限がなければスクリプトは途中で失敗します。1. ファイルの存在と読み取り権限
require_file() { local path="$1" if [[ ! -f "${path}" ]]; then echo "[ERROR] ファイルが存在しません: ${path}" >&2 exit 1 fi if [[ ! -r "${path}" ]]; then echo "[ERROR] ファイルを読み取れません(権限不足): ${path}" >&2 exit 1 fi }
-f は「通常ファイルとして存在するか」を確認します。シンボリックリンクが壊れている場合も -f は false を返すため、リンク切れを拾えます。-r は読み取り権限の確認です。root で実行しているか一般ユーザーかによって結果が変わる点に注意してください(root は多くのケースで -r が true になります)。2. 出力先ディレクトリの存在と書き込み権限
バックアップの出力先ディレクトリは、存在しない場合に作るか、なければエラーにするかを設計で決めます。# 存在しなければ作成する設計 require_dir() { local path="$1" if [[ ! -d "${path}" ]]; then echo "[INFO] ディレクトリを作成します: ${path}" mkdir -p "${path}" || { echo "[ERROR] ディレクトリの作成に失敗しました: ${path}" >&2 exit 1 } fi if [[ ! -w "${path}" ]]; then echo "[ERROR] ディレクトリに書き込めません(権限不足): ${path}" >&2 exit 1 fi }
mkdir -p 失敗時に || { ... exit 1; } で捕捉するのがポイントです。set -e を使っていれば自動的に落ちますが、エラーメッセージが「mkdir: ... : Permission denied」だけでは文脈がわかりません。自分でメッセージを用意することで、どのスクリプトのどの処理で失敗したかが一目でわかるログになります。ディスク空き容量のチェック
特に大きなアーカイブを生成するスクリプトでは、容量不足による途中失敗が致命的です。処理開始前に「最低このくらいは空きが必要」という閾値チェックを入れておきます。# 引数: パス, 最小必要容量(MB) require_disk_space() { local path="$1" local min_mb="$2" # dfの出力から使用可能なブロック数(KB単位)を取得 local avail_kb avail_kb=$(df -k "${path}" | awk 'NR==2 {print $4}') local avail_mb=$(( avail_kb / 1024 )) if [[ "${avail_mb}" -lt "${min_mb}" ]]; then echo "[ERROR] ディスク空き容量が不足しています。" >&2 echo " パス: ${path}" >&2 echo " 必要: ${min_mb} MB / 現在の空き: ${avail_mb} MB" >&2 exit 1 fi echo "[INFO] ディスク空き容量: ${avail_mb} MB (必要: ${min_mb} MB) - OK" }
$ require_disk_space /var/backup 500 [INFO] ディスク空き容量: 12480 MB (必要: 500 MB) - OK
df -k の NR==2 はヘッダー行を除いた1行目(マウントポイントの情報行)を取得します。出力先パスが長い場合は行が折り返されて NR==3 になることがあります。より安全なのは df -k --output=avail "${path}" | tail -1 ですが、--output は一部の古い環境でサポートされていない点に注意してください(Rocky Linux 9.4 / Ubuntu 24.04 では使用可能です)。環境変数と必須設定値の検証
スクリプトが外部から設定値を受け取る場合(設定ファイルの source・環境変数)、その値が空でないことを確認します。# 環境変数が空でないことを確認する関数 require_env() { local varname="$1" local value="${!varname}" # 変数名から値を間接参照 if [[ -z "${value}" ]]; then echo "[ERROR] 環境変数 ${varname} が設定されていません。" >&2 exit 1 fi } # 使い方(設定ファイルをsourceした後) source /etc/backup.conf require_env BACKUP_SRC require_env BACKUP_DEST require_env BACKUP_GENERATIONS
${!varname} はBashの間接参照です。require_env "BACKUP_SRC" と呼ぶと、実際には ${BACKUP_SRC} の値を参照します。設定項目が増えてもコードを変えずに対応できます。完成形:preflight_check 関数を1つにまとめる
ここまで作った4つの検証を1つのpreflight_check 関数に集約すると、スクリプトの冒頭に1行書くだけで全項目を確認できます。#!/bin/bash # backup.sh — 週次フルバックアップスクリプト set -euo pipefail # -------- 設定 -------- BACKUP_SRC="/var/www/html" BACKUP_DEST="/mnt/backup" BACKUP_GENERATIONS=7 MIN_DISK_MB=1024 # 最小空き容量(MB) # -------- preflight チェック -------- preflight_check() { echo "=== preflight チェック開始 ===" # 1. 必要なコマンドの確認 check_required_cmds tar gzip rsync # 2. バックアップ元の確認 require_dir "${BACKUP_SRC}" # 3. バックアップ先の確認(なければ作成) require_dir "${BACKUP_DEST}" # 4. ディスク容量の確認 require_disk_space "${BACKUP_DEST}" "${MIN_DISK_MB}" echo "=== preflight チェック完了 — 処理を開始します ===" } # -------- メイン処理 -------- main() { preflight_check local timestamp timestamp=$(date +%Y%m%d_%H%M%S) local archive="${BACKUP_DEST}/backup_${timestamp}.tar.gz" echo "[INFO] バックアップ開始: ${BACKUP_SRC} -> ${archive}" tar -czf "${archive}" -C "$(dirname "${BACKUP_SRC}")" "$(basename "${BACKUP_SRC}")" echo "[INFO] 完了: ${archive}" } main "$@"
$ bash backup.sh === preflight チェック開始 === [INFO] ディスク空き容量: 38400 MB (必要: 1024 MB) - OK === preflight チェック完了 — 処理を開始します === [INFO] バックアップ開始: /var/www/html -> /mnt/backup/backup_20260910_023001.tar.gz [INFO] 完了: /mnt/backup/backup_20260910_023001.tar.gz
-C でディレクトリを変えてアーカイブする方法など)はtar コマンドの実用例を参照してください。require_cmd・require_dir・require_disk_space・require_env を共通ライブラリファイル(例:
/etc/scripts/lib/preflight.sh)に書き出して source で読み込む設計にすれば、複数のスクリプト間で使い回せます。シェルスクリプトによる自動化の設計パターンをまとめて学ぶなら、シェルスクリプト実践講座 も参考にしてみてください。トラブルシュート
「df: invalid option -- 'output'」が出る
CentOS 7 や古い RHEL 環境ではdf --output=avail が使えません。代わりに awk 'NR==2 {print $4}' を使う方式(本記事の例)で対応してください。「[ERROR] ディレクトリに書き込めません」が出るのに ls では権限がある
実行ユーザーとファイルの所有者グループが一致していない場合や、NFS/SMBマウントのマウントオプションで書き込みが制限されている場合に起こります。ls -la で所有者を確認し、実行ユーザーと一致しているかを確認してください。require_env で「-bash: !varname: event not found」が出る
対話型シェルでヒストリ展開が有効な場合に起きます。set +H を先に実行するか、スクリプトファイルとして実行(bash script.sh)すれば問題ありません。本記事のまとめ
| 確認項目 | 使う方法 | ポイント |
|---|---|---|
| コマンドの存在 | command -v cmd |
which より信頼性が高い |
| ファイルの存在・読み取り | [[ -f path ]] / [[ -r path ]] |
シンボリックリンク切れも検出 |
| ディレクトリの存在・書き込み | [[ -d path ]] / [[ -w path ]] |
なければ mkdir -p で作成も可能 |
| ディスク空き容量 | df -k + awk |
MB単位で閾値と比較する |
| 環境変数・設定値 | ${!varname} 間接参照 |
設定ファイル source 後に確認する |
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:シェルスクリプトにロングオプションを実装する方法|getoptコマンドで--verbose・--outputを安全に処理する設計
- この記事の属するカテゴリ:シェルスクリプトへ戻る

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