シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Linuxtips, シェルスクリプト > シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで
「毎晩バックアップを手動で動かすのは面倒だ」 「cronで自動化したいけど、エラーが出ても気づけない」 そんな悩みを抱えているサーバー管理者は多いはずです。

バックアップの自動化は、シェルスクリプトとcronを組み合わせるだけで実現できます。ただし、動けばOKのスクリプトと、現場で長期間安定稼働するスクリプトには大きな差があります。

この記事では、シェルスクリプトでバックアップ処理を書き、cronで定期実行し、ログに残すところまでを実践的に解説します。RHEL 9.4 / Ubuntu 24.04 LTSで動作確認済みのコードを使って、コピーして使えるレベルまで仕上げます。

この記事のポイント

・シェルスクリプトでrsyncを使ったバックアップ処理を安全に書ける
・cronで定期実行するときの環境変数・パスの落とし穴を避けられる
・ログファイルにタイムスタンプ付きで記録し、エラーを判定できる
・バックアップの世代管理(古いファイルの自動削除)を実装できる


シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで

「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

なぜシェルスクリプトでバックアップを自動化するのか

手動バックアップの最大の問題は「忘れる」ことです。深夜のメンテナンス後、重要な作業前のバックアップを取り忘れてトラブルになったケースは数え切れません。

私がセミナーで3,100名以上を指導してきた中で、バックアップが原因で復旧できなかったという相談を何度も受けてきました。ほぼ全てのケースで共通しているのが「自動化していなかった」という点です。

シェルスクリプトとcronを組み合わせることで、人間の手を介さずに定期実行できます。さらに以下のメリットがあります。

一貫性:毎回同じ手順でバックアップが取れる
記録:ログに残すことで後から確認できる
検知:終了コードでエラーを即座に把握できる
世代管理:古いバックアップを自動削除してディスクを節約できる


シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで - 解説1

バックアップスクリプトの基本構成を作る

1. ディレクトリ構成と変数定義

最初に変数と保存先を整理します。後から変更しやすい設計にすることが重要です。

#!/bin/bash # backup.sh -- ディレクトリバックアップスクリプト(RHEL 9.4 / Ubuntu 24.04 確認済み) # ---- 設定変数 ---- SRC_DIR="/var/www/html" # バックアップ元 DST_DIR="/mnt/backup" # バックアップ先 LOG_DIR="/var/log/backup" # ログ保存先 TIMESTAMP=$(date +%Y%m%d_%H%M%S) # タイムスタンプ LOG_FILE="${LOG_DIR}/backup_${TIMESTAMP}.log" KEEP_DAYS=14 # 保持する日数 # ---- ディレクトリ確認 ---- mkdir -p "${DST_DIR}" "${LOG_DIR}"

変数を先頭にまとめておくことで、バックアップ元やバックアップ先を変更する際にスクリプト全体を読む必要がありません。保守性が大きく上がります。

2. rsyncを使ったコピー処理

バックアップの実体となるrsyncコマンドを書きます。

# ---- バックアップ実行 ---- echo "[$(date)] バックアップ開始: ${SRC_DIR} -> ${DST_DIR}" | tee -a "${LOG_FILE}" rsync -av --delete "${SRC_DIR}/" "${DST_DIR}/" >> "${LOG_FILE}" 2>&1 EXIT_CODE=$? echo "[$(date)] rsync 終了コード: ${EXIT_CODE}" | tee -a "${LOG_FILE}"

-a(--archive):パーミッション・タイムスタンプ・シンボリックリンクを保持する
-v(--verbose):転送ファイルを出力してログに残す
--delete:バックアップ元で削除されたファイルをバックアップ先からも削除する
2>&1:標準エラーを標準出力にまとめてログファイルに書き込む

rsyncのオプションは組み合わせで動作が変わります。--deleteを外すと削除が反映されない「増分バックアップ」になります。用途に合わせて選んでください。

3. 終了コードでエラーを判定する

rsyncの終了コード($?)を確認してエラー処理を書きます。

# ---- 終了コード判定 ---- if [ "${EXIT_CODE}" -eq 0 ]; then echo "[$(date)] バックアップ成功" | tee -a "${LOG_FILE}" elif [ "${EXIT_CODE}" -eq 24 ]; then # 転送中にファイルが消えた(正常範囲) echo "[$(date)] 警告: 一部ファイルが転送中に消えました(終了コード24)" | tee -a "${LOG_FILE}" else echo "[$(date)] エラー: rsync 失敗(終了コード ${EXIT_CODE})" | tee -a "${LOG_FILE}" exit 1 fi

rsyncの終了コード24は「転送中にソースファイルが消えた」状態です。ログが高速にローテーションする環境では頻繁に発生します。エラーではなく警告として扱う設計にしておくと、不要なアラートが減ります。

古いバックアップを自動削除する世代管理

バックアップが溜まり続けるとディスクが枯渇します。findコマンドで古いログ・バックアップを定期削除しましょう。

# ---- 古いログを削除(14日以上前) ---- find "${LOG_DIR}" -name "backup_*.log" -mtime +${KEEP_DAYS} -delete echo "[$(date)] ${KEEP_DAYS}日以上前のログを削除しました" | tee -a "${LOG_FILE}"

rsync --deleteを使ったミラーリング型では1世代分しか保持しないのでログだけ削除すればOKです。複数世代を保持したい場合は、DST_DIRにタイムスタンプを含めたサブディレクトリを作り、古いサブディレクトリをfindで削除します。


シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで - 解説2

cronで定期実行するときの落とし穴

スクリプトが手動では動くのにcronでは動かない、という問題はシェルスクリプトの落とし穴としてよく知られています。

4. cronの環境変数とPATHの問題

cronは非常に限定的な環境で動作します。通常のログインシェルとは異なり、PATH・LANG・ホームディレクトリの変数がほとんど設定されていません。

# crontab -e で設定する内容(毎日 2:00 に実行) # cronのデフォルトPATHは /usr/bin:/bin のみ PATH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin SHELL=/bin/bash 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup/cron.log 2>&1

crontabの先頭でPATHを明示的に設定しておくことを強く推奨します。rsyncやその他のコマンドが「command not found」になる原因の大半はPATHの問題です。

また、スクリプト内のコマンドはフルパスで書くと安全です。rsync ではなく /usr/bin/rsync のように書けば、cronのPATH設定に依存しません。

5. スクリプトに実行権限を付ける

スクリプトをcronに登録する前に、実行権限を確認してください。

# スクリプトを /usr/local/bin/ に配置 sudo cp backup.sh /usr/local/bin/backup.sh sudo chmod 700 /usr/local/bin/backup.sh sudo chown root:root /usr/local/bin/backup.sh # 動作確認(root権限でテスト実行) sudo /usr/local/bin/backup.sh # ログを確認 ls -lh /var/log/backup/

chmod 700で「所有者のみ実行可能」に設定します。バックアップスクリプトはシステム全体に影響するため、一般ユーザーには実行させないのが鉄則です。

セミナーでよく聞かれる質問が「なぜ755ではなく700にするのか」です。バックアップスクリプトは内部に変数でパスを持ち、誤操作で別の場所を上書きしてしまうリスクがあります。root専用に絞っておくことで事故を防ぎます。

スクリプト完成版(コピーして使えるコード)

ここまでのパーツを組み合わせた完成版スクリプトです。

#!/bin/bash # backup.sh -- バックアップ自動化スクリプト # 対象OS: RHEL 9.4 / Ubuntu 24.04 LTS # 用途: Webサーバーコンテンツの日次バックアップ set -euo pipefail # エラー・未定義変数・パイプ失敗で即停止 # ---- 設定変数 ---- SRC_DIR="/var/www/html" DST_DIR="/mnt/backup" LOG_DIR="/var/log/backup" TIMESTAMP=$(date +%Y%m%d_%H%M%S) LOG_FILE="${LOG_DIR}/backup_${TIMESTAMP}.log" KEEP_DAYS=14 # ---- 初期化 ---- mkdir -p "${DST_DIR}" "${LOG_DIR}" echo "[$(date)] === バックアップ開始 ===" echo "[$(date)] 元: ${SRC_DIR}" | tee -a "${LOG_FILE}" echo "[$(date)] 先: ${DST_DIR}" | tee -a "${LOG_FILE}" # ---- rsync実行 ---- /usr/bin/rsync -av --delete "${SRC_DIR}/" "${DST_DIR}/" >> "${LOG_FILE}" 2>&1 EXIT_CODE=$? if [ "${EXIT_CODE}" -eq 0 ]; then echo "[$(date)] バックアップ完了" | tee -a "${LOG_FILE}" elif [ "${EXIT_CODE}" -eq 24 ]; then echo "[$(date)] 警告: 転送中ファイル消失(終了コード24)" | tee -a "${LOG_FILE}" else echo "[$(date)] エラー: rsync 失敗(終了コード ${EXIT_CODE})" | tee -a "${LOG_FILE}" exit 1 fi # ---- 世代管理 ---- /usr/bin/find "${LOG_DIR}" -name "backup_*.log" -mtime +${KEEP_DAYS} -delete echo "[$(date)] ${KEEP_DAYS}日超のログを削除しました" | tee -a "${LOG_FILE}" echo "[$(date)] === 処理終了 ===" | tee -a "${LOG_FILE}"

冒頭の set -euo pipefail は現場スクリプトの必須設定です。eはエラーで即停止、uは未定義変数の参照をエラー扱い、pipefailはパイプ途中のエラーを拾います。この1行があるだけで、無言で失敗し続けるスクリプトを防げます。

関連記事として、シェルスクリプトのエラー処理全般については「Linux 基本コマンドの解説」も合わせて参照してください。


シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで - 解説3

ログファイルの設計と実サーバーでの出力例

ログがきちんと残っているか、実際のサーバーでの出力を見てみましょう。

# 実行後のログ出力例(実サーバー: web01.example.com) [Tue Jun 24 02:00:03 JST 2026] 元: /var/www/html [Tue Jun 24 02:00:03 JST 2026] 先: /mnt/backup sending incremental file list ./ index.html wp-content/uploads/2026/06/image001.png wp-content/uploads/2026/06/image002.png sent 1,234,567 bytes received 128 bytes 2,469,390.00 bytes/sec total size is 125,432,890 speedup is 101.58 [Tue Jun 24 02:00:09 JST 2026] バックアップ完了 [Tue Jun 24 02:00:09 JST 2026] 14日超のログを削除しました [Tue Jun 24 02:00:09 JST 2026] === 処理終了 ===

ログに開始時刻・終了時刻・転送サイズが残ることで、何秒かかったか、どのファイルが更新されたかを後から確認できます。バックアップが取れているか確認したいとき、このログが証拠になります。

ディスク使用量の確認は ls コマンドの基本オプション で確認できます。バックアップ先の容量監視も合わせて行いましょう。

トラブルシュート|よくあるエラーと対処法

「rsync: change_dir failed」が出る

バックアップ元ディレクトリが存在しないか、権限がありません。スクリプト実行ユーザーが対象ディレクトリを読み取れるか確認してください。

# ディレクトリ存在確認 ls -la /var/www/html # 実行ユーザー確認 whoami # パーミッション確認 stat /var/www/html

cronからは動かないが手動では動く

最も多いのがPATHの問題です。which rsync でrsyncのフルパスを確認し、スクリプト内をフルパスに書き換えてください。

# rsyncのフルパス確認 which rsync # 出力例: /usr/bin/rsync # cron同等の環境でPATH確認 env -i /bin/sh -c env | grep PATH

バックアップ先ディスクが容量不足

KEEP_DAYSを短くするか、世代管理の削除処理が動いているかログで確認してください。

# ディスク使用量確認 df -h /mnt/backup # 古いファイルのサイズ確認(14日以上前) find /mnt/backup -mtime +14 -ls | awk '{sum += $7} END {print sum/1024/1024 " MB"}'


シェルスクリプトでバックアップ自動化を作る実践|cronとログ設計まで - まとめ

本記事のまとめ

シェルスクリプトでバックアップを自動化する手順と、cronで安定稼働させるポイントを解説しました。

やりたいこと 実装方法
ファイル一式をバックアップする rsync -av --delete SRC/ DST/
cronで毎日2時に実行する crontab -e0 2 * * * /path/to/backup.sh
ログにタイムスタンプを付けて記録する tee -a "${LOG_FILE}" で出力を追記
エラーで即停止させる set -euo pipefail をスクリプト冒頭に記述
古いログを自動削除する find LOG_DIR -name "*.log" -mtime +14 -delete
バックアップは「動けばOK」ではなく、障害発生時に確実に復旧できる設計が重要です。本記事で紹介したスクリプトをベースに、自分のサーバー環境に合わせてカスタマイズしてください。

シェルスクリプトを体系的に学びませんか?

バックアップ自動化のスクリプトをきちんと書けるようになるには、変数・条件分岐・エラー処理の基礎設計を体系的に習得することが近道です。
ネットの切れ端の情報をコピペするだけでなく、現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。