このページでは、シェルスクリプトを使って、ディレクトリ単位での安全なデータ転送を自動化する方法を解説します。
rsyncによる完全転送とmvによるアトミックなリネーム処理を組み合わせることで、処理の中断や重複転送、破壊的な上書きを防ぐ設計になっています。
ファイル単位の転送とは異なり、ディレクトリ構造を丸ごと扱うことで、業務システムのバックアップや中継処理にも柔軟に対応可能です。設計思想から具体的な実装、活用例まで、現場で役立つポイントを整理してお届けします。
ディレクトリ転送の自動化が必要な背景
サーバー運用やシステム連携の現場では、複数ファイルを含むディレクトリ単位でのデータ転送が必要になることが多いです。
特にバックアップや中間データの管理では、手動作業ではミスや転送漏れが発生しやすいため、処理の自動化が重要です。安定して一括転送できる仕組みを構築することで、運用効率と信頼性を高められます。
一括処理の安定性とログの重要性
ディレクトリ全体をまとめて転送する場合、一連の処理が確実に完了したことを確認できるログが必要です。
ログ記録により、いつ処理が開始され終了したのか、異常発生時の状況も把握できるため、運用監視やトラブル対応が容易になります。自動化スクリプトはログ出力を組み込み、安定稼働を支える土台となります。
logOut "INFO" "Starting rsync to temporary directory: $tmp_dir"
logOut "INFO" "Directory transfer completed successfully."
ファイル単位ではなくディレクトリ単位である理由
ファイル単位の転送は管理が煩雑になりやすく、整合性を保つのが難しいことがあります。対してディレクトリ単位での転送は構造が保持され、処理ミスを減らせます。
特に業務システムで扱う中間データや処理結果はディレクトリ単位で管理されるため、そのままの単位で扱うほうが現場運用に適しています。
本スクリプトは、一時ディレクトリへ完全コピーを行い、その後アトミックに正式なディレクトリ名へリネームする設計です。この手法により、途中で処理が中断しても不完全な状態が残らず、安全に運用できます。
スクリプト構成とディレクトリの前提
本スクリプトは、ディレクトリ単位での安全な転送処理を目的に設計されています。
転送元と転送先のディレクトリを指定し、コピーまたは移動の処理モードを選択できる構成です。転送先ディレクトリ内に転送元ディレクトリ名で配置され、一時ディレクトリを経由してアトミックなリネームを行うことで、処理の中断による不整合を防止しています。
引数と処理モードの定義
本スクリプトは、以下の3つの引数を受け取ります。
引数
- 転送元ディレクトリ(-d)
- 転送先のベースディレクトリ(-t)
- 処理モード(-m)
処理モードは数字で指定し、0がコピー、1が移動を意味します。
引数の妥当性チェックでは、ディレクトリの存在確認やワイルドカード禁止、親子関係による危険な設定の検出も行っています。
変数と一時ディレクトリの使い分け
転送元ディレクトリのパスは変数に格納し、転送先はベースディレクトリの下に転送元名のディレクトリが作成されます。
処理中は転送先ディレクトリの隣に「.tmpdir」という一時ディレクトリを作り、rsyncで完全同期を行います。 転送完了後、一時ディレクトリを正式ディレクトリ名にリネームすることで、転送中の状態が外部から見えないように設計されています。
ログ出力とエラー制御の仕様
ログは共通のログ出力関数を利用しており、処理の各段階で開始や成功、異常の状態をログに記録します。
rsyncの終了コードやディレクトリ操作の失敗はログにエラーとして残し、異常時にはスクリプトを終了させる制御が実装されています。 これにより運用時の監視やトラブルシュートがしやすくなっています。
変数名 | 説明 |
---|---|
src_dir | 転送元ディレクトリのパス |
dst_dir | 転送先のベースディレクトリのパス |
mode | 処理モード(0=コピー、1=移動) |
src_base | 転送元ディレクトリの末尾の名前部分 |
dst_base | 転送先のベースディレクトリ配下の転送元ディレクトリ名の完全パス |
tmp_dir | 転送先の一時ディレクトリパス(dst_base.tmpdir) |
処理フロー全体の構造
本スクリプトは、処理の流れを事前処理(pre-process)、メイン処理(main-process)、事後処理(post-process)の3段階に分けて設計されています。
各段階で必要なチェックや操作を明確に分離することで、堅牢で保守しやすい構造になっています。特にメイン処理ではrsyncを用いたディレクトリの完全同期と、その後のアトミックなリネーム処理によって安全性を確保しています。
pre-processでの引数チェックと事前ログ
事前処理では、引数の受け取りと妥当性チェックを行います。 ここで転送元ディレクトリ、転送先ディレクトリ、処理モード(コピーか移動か)が正しく指定されているか検証します。
ワイルドカードの禁止や、親子ディレクトリ構造の不正検出もこの段階で行い、不正な条件ではスクリプトを終了させます。 また、開始時のログ出力により処理の起点を記録します。
logOut "DEBUG" "src=/path/to/src, dst=/path/to/dst, mode=0"
main-processでのrsyncとアトミックなmv処理
メイン処理では、まず転送先の一時ディレクトリ(例: dst_base.tmpdir)に対してrsyncを用いて完全な同期を行います。 この同期はチェックサム比較と削除オプションを使い、転送元と転送先を完全に一致させます。
rsync --checksum --delete -av "$src_base/" "$tmp_dir/"
同期が成功した後は、既存の正式ディレクトリがあれば削除し、一時ディレクトリを正式ディレクトリ名にリネームします。
このリネーム操作はアトミックで行われるため、転送途中の不整合が外部に見えない設計です。
mv -f "$tmp_dir" "$dst_base"
処理モードが移動の場合は、転送元ディレクトリを削除して処理を完結させます。
rm -rf "$src_base/"
post-processでの後処理と成功ログ出力
事後処理では、処理の正常終了をログに記録します。 このログ出力は運用監視やトラブル解析に役立ち、スクリプトの信頼性を高めます。 異常時はメイン処理段階で即座にログを残して終了するため、ここでは成功時のメッセージのみ記録されます。
logOut "INFO" "Directory transfer completed successfully."
基本的な使い方と注意点
このセクションでは、ディレクトリ単位での転送スクリプトの基本的な使い方と、運用時に注意すべきポイントについて解説します。コピーと移動の違いや誤操作防止の工夫、さらに応用的な活用例を紹介します。
前提となる実行環境
Beエンジニアでシェルスクリプトを実行する環境は下記の通りとします。
実行環境
BASE_DIR(任意のディレクトリ)
- scripts
- bin(実行スクリプト格納領域)
- <<各種実行スクリプト>>.sh (実行ファイル)
- com(共通スクリプト格納領域)
- logger.shrc(共通ログ出力ファイル)
- utils.shrc(共通関数定義ファイル)
- etc(設定ファイル等の格納領域)
- infraMessage.conf(メッセージ定義ファイル)
- log(スクリプト実行ログの格納領域)
- スクリプト名.log
- tmp(テンポラリ領域)
- rep(レポート出力領域)
- bin(実行スクリプト格納領域)
ディレクトリ転送スクリプト
ディレクトリ転送スクリプトの全体コードを以下に掲載します。処理の流れや各部分の役割を理解しやすいよう、構造的に記述されたスクリプトです。実際の運用やカスタマイズの参考にしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
#!/bin/sh # ------------------------------------------------------------------ # 関数名 :dirTransfer.sh # 概要 :ディレクトリ単位の信頼性の高い転送(コピー/移動) # 説明 : # rsyncを使用して一時ディレクトリへ完全転送し、完了後にrenameでアトミックに入れ替える。 # - コピー(mode=0)または移動(mode=1)に対応。 # - 転送先に同名ディレクトリが存在する場合は削除して上書き。 # - 親子ディレクトリ誤設定による破壊的動作を防ぐ安全設計。 # # 引数 : # -d <source_dir> :転送元ディレクトリ # -t <target_dir> :転送先のベースディレクトリ(直下に配置される) # -m <mode> :0=copy, 1=move(数字で指定) # # 戻り値 :0=成功, 2=異常 # 使用箇所 :ファイル連携、バックアップ、ディレクトリ同期等 # ------------------------------------------------------------------ #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # <変更履歴> # Ver. 変更管理No. 日付 更新者 変更内容 # 1.0 PR-0001 2025/07/30 Bepro 新規作成 #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # 共通クラスの読み込み . "$(dirname "$0")/../com/utils.shrc" . "$(dirname "$0")/../com/logger.shrc" setLANG utf-8 runAs root "$@" # ------------------------------------------------------------------ # variables (変数の宣言領域) # ------------------------------------------------------------------ scope="var" # ======================================== # 定数定義 # ======================================== readonly JOB_OK=0 readonly JOB_WR=1 readonly JOB_ER=2 # ---------------------------------------------------------- # functions (関数を記述する領域) # ---------------------------------------------------------- scope="func" # ------------------------------------------------------------------ # 関数名 :usage # 概要 :使用方法の表示 # 説明 : # 引数が不足または不正な場合に呼び出され、実行を終了させる。 # 引数 :なし # 戻り値 :なし(exit 2) # 使用箇所 :引数パース後のチェック # ------------------------------------------------------------------ usage() { echo "Usage: $0 -f <source_dir> -t <target_dir> -m <0|1>" echo " -d source directory" echo " -t destination directory" echo " -m mode: 0 = copy, 1 = move" exit ${JOB_ER} } # ------------------------------------------------------------------ # checkArgs # 概要 :引数の妥当性を検証 # 説明 : # ディレクトリ存在確認、ワイルドカード排除、親子関係の検出など # 引数 :$1 = src_dir, $2 = dst_dir, $3 = mode # 戻り値 :なし(エラー時 exit) # 使用箇所 :main前の検証ステップ # ------------------------------------------------------------------ checkArgs() { if [ $# -lt 3 ]; then logOut "ERROR" "Insufficient arguments." usage fi if echo "$1" | grep -q '[*?]' || echo "$2" | grep -q '[*?]'; then logOut "ERROR" "Wildcard characters are not allowed in path." exit ${JOB_ER} fi if [ ! -d "$1" ]; then logOut "ERROR" "Source directory does not exist: $1" exit ${JOB_ER} fi if [ ! -d "$2" ]; then logOut "ERROR" "Target directory does not exist: $2" exit ${JOB_ER} fi if echo "$3" | grep -q '[^01]'; then logOut "ERROR" "Mode must be 0 (copy) or 1 (move): $3" exit ${JOB_ER} fi src_tmp="${1%/}" src_parent="${src_tmp%/*}" dst_chk="${2#${src_tmp}}" if [ -z "$dst_chk" ] || [ "$2" != "$dst_chk" ] || [ "${2%/}" = "$src_parent" ]; then logOut "ERROR" "Invalid parent-child directory relationship." exit ${JOB_ER} fi } # ------------------------------------------------------------------ # pre-process (事前処理ロジックを記述する領域) # ------------------------------------------------------------------ scope="pre" src_dir="" dst_dir="" mode="" while getopts d:t:m: opt; do case "$opt" in d) src_dir="$OPTARG" ;; t) dst_dir="$OPTARG" ;; m) mode="$OPTARG" ;; *) usage ;; esac done if [ "$OPTIND" -eq 1 ]; then logOut "ERROR" "No arguments provided." usage fi logOut "DEBUG" "src=$src_dir, dst=$dst_dir, mode=$mode" checkArgs "$src_dir" "$dst_dir" "$mode" # ------------------------------------------------------------------ # main-process (メインロジックを記述する領域) # ------------------------------------------------------------------ scope="main" src_base="${src_dir%/}" dst_base="${dst_dir%/}/${src_base##*/}" tmp_dir="${dst_base}.tmpdir" logOut "INFO" "Starting rsync to temporary directory: $tmp_dir" rsync --checksum --delete -av "$src_base/" "$tmp_dir/" if [ $? -ne 0 ]; then logOut "ERROR" "rsync failed." exit ${JOB_ER} fi if [ -d "$dst_base" ]; then logOut "INFO" "Removing existing destination: $dst_base" rm -rf "$dst_base/" fi logOut "INFO" "Renaming $tmp_dir to $dst_base" mv -f "$tmp_dir" "$dst_base" if [ "$mode" -eq 1 ]; then logOut "INFO" "Removing source directory after move: $src_base" rm -rf "$src_base/" fi # ---------------------------------------------------------- # post-process (事後処理ロジックを記述する領域) # ---------------------------------------------------------- scope="post" logOut "INFO" "dirTransfer.sh completed successfully." exit ${JOB_OK} |
コピーと移動の違いを活かした運用パターン
転送スクリプトは、処理モードとしてコピー(0)と移動(1)を指定できます。
コピーの場合は転送元のディレクトリはそのまま残り、転送先に内容が複製されます。これにより、バックアップや検証用の複製を作成する用途に適しています。 移動の場合は転送元のディレクトリが削除されるため、処理対象を明確に切り替えたい場面で効果的です。
sh script.sh -d /path/source -t /path/destination -m 0 # 転送元のディレクトリはそのまま残る
sh script.sh -d /path/source -t /path/destination -m 1 # 転送先に内容が複製されたら元ディレクトリを削除する
誤操作防止のための設計上の工夫
スクリプトでは、転送元と転送先の親子関係を厳格にチェックし、不正なディレクトリ指定による破壊的な動作を防止しています。 また、ワイルドカードの使用を禁止することで、予期せぬファイルやディレクトリの転送を避けています。 これらの制御により、安全に運用できる仕組みが整っています。
応用例:バックアップやテスト環境との連携
本スクリプトはバックアップ処理やテスト環境への同期など多様な用途に活用可能です。 たとえば、業務データを定期的に別サーバーへコピーして障害時のリカバリに備えたり、開発環境での検証用データを同期したりすることができます。 安全なアトミック処理により、データの整合性を確保しつつ効率的な運用を実現します。