はじめに
もともとGit管理されているリポジトリがあったわけだが、引継ぎ時点で100以上のunstagedなログやらプログラムのバックアップがあった。これから先もこうしていくと気が重くなる。とりあえず、だれでも使えそうな(Gitとは無関係の)バックアップスクリプトを作ってみた。
のを放置していていたので、とりあえずどっかに貼っとかないと永遠に忘れると思い、メモ掲載。
書式
書式: backup.sh [オプション....] [対象バックアップファイル]
オプション:
-q 静止モード。コミットメッセージは quiet modeと書かれる
-d デバッグモード。処理内容を確認できる。
-l 履歴を見る
-b 自動モード。何も出力せず、コミットメッセージにはバックアップ日時が書かれる。
-p [mesg] -bと併用の際、日時の手前に書く
ユースケース
-
crontabにbackup.sh -b -p 'auto' path/to/file.cppとか書いて自動バックアップ - 重要なバックアップはメッセージ書く
履歴をみたいときは、 -l する
直近の履歴と同じハッシュならバックアップしない
mysrv 11:25:48 > ./backup.sh backup.sh
INFO:: ハッシュ値照合中... [backup.sh.20221122d.bk]
INFO:: バックアップファイルと同じハッシュのファイルなため、バックアップを実施しません。
mysrv 11:26:27 >
スクリプト本体
#!/bin/bash -eu
###############################################
# File based backup
# Author: Anya-tokugawa
cat << COMMENT > /dev/null
機能:
- 日時+アルファベット順+アルファベット文字数でバックアップ
例:
ver1 hoge.sh.20210103a.bk
ver2 hoge.sh.20210105a.bk
ver3 hoge.sh.20210105b.bk
ver4 hoge.sh.20210105c.bk
...
hoge.sh.20210105z.bk
hoge.sh.20210105aa.bk
hoge.sh.20210105ab.bk
...
hoge.sh.20210105at.bk
hoge.sh.20210106a.bk
...
latest hoge.sh
- 一日最大, a-zzzzまで可能。1ファイル475254回可能。一秒間に5回バックアップ可能。
TODO:
- バックアップデータに関するハッシュリポジトリを作る。
- バックアップデータ自体はリポジトリに格納しリンクだけする?
- 同じハッシュのファイルを集約
- コミットメッセージの実装(済)
- 削除されたバックアップファイルの自動検出
- 複数行のコミットメッセージをつくる。
- backupDirにしてHashのファイルを作る。
- 直前とのdiff/colordiffの実装
COMMENT
INFO=1
AUTO=0
DEBUG=0
VIEW=0
mesgPrefix=""
##############################
# リポジトリ設定
INFOFILE=".backup.csv"
if [[ ! -f "$INFOFILE" ]];then
echo -n '' > "$INFOFILE"
fi
##############################
_LOG(){
if [[ "$1" == "ERROR" ]];then
shift
if [[ $INFO -ne 0 ]];then
echo "ERROR:: $*"
fi
exit 1
fi
if [[ "$1" == "INFO" ]] && [[ $INFO -eq 1 ]];then
shift
echo "INFO:: $*"
elif [[ "$1" == "DEBUG" ]] && [[ $DEBUG -eq 1 ]];then
shift
echo "DEBUG:: $*"
fi
}
# 19990101a方式のバージョンソート
_sort(){
while read -r line
do
echo "${#line} $line"
done < /dev/stdin | sort -n | cut -d " " -f 2-
}
# バックアップ処理
_backup(){
if [[ $INFO -eq 1 ]] && [[ $AUTO -eq 0 ]];then
rsync -z --progress "$1" "$2"
else
rsync -qz --progress "$1" "$2"
fi
status_code=$?
if [[ $status_code -eq 0 ]];then
_LOG DEBUG 書き込み禁止化
chmod a-w "$2"
mesg=""
if [[ $INFO -eq 1 ]] && [[ $AUTO -eq 0 ]];then
echo -n "メッセージ: "
read -r mesg
elif [[ $AUTO -eq 1 ]];then
if [[ "$mesgPrefix" == "" ]];then
mesg="$(date)"
else
mesg="$mesgPrefix $(date)"
fi
else
mesg="quit-mode"
fi
chmod +w $INFOFILE
# baseFileName, VersionStr,HASH, mesg
echo "${1},${3},${4},${mesg}" >> "$INFOFILE"
chmod -w $INFOFILE
fi
}
_usage(){
echo "書式: $0 [オプション....] [対象バックアップファイル]"
echo "オプション:"
echo " -q 静止モード。コミットメッセージは quiet modeと書かれる"
echo " -d デバッグモード。処理内容を確認できる。"
echo " -l 履歴を見る"
echo " -b 自動モード。何も出力せず、コミットメッセージにはバックアップ日時が書かれる。"
echo " -p [mesg] -bと併用の際、日時の手前に書く。"
}
if [[ $# -lt 1 ]];then
_usage
exit 1
fi
NOW="$1"
if [[ $# -gt 1 ]];
then
while [[ $# -ne 0 ]];
do
NOW="$1"
case "$NOW" in
"-d") DEBUG=1;;
"-l") VIEW=1;;
"-q") INFO=0;;
"-b") AUTO=1; INFO=0; DEBUG=0;;
"-p") mesgPrefix="$2";;
esac
shift
done
fi
BackupFile="$NOW"
_LOG DEBUG ファイルの存在チェック
if [[ ! -f "${BackupFile}" ]];then
_LOG ERROR 対象バックアップファイルが存在しません。
fi
_LOG DEBUG バックアップファイルか
if [[ "${BackupFile}" =~ .*\.bk ]];then
_LOG ERROR バックアップファイルはバックアップできません。
fi
if [[ $VIEW -eq 1 ]];then
HEADER="バックアップファイル,バージョン,SHA1SUMハッシュ値,メッセージ"
cat <(echo "$HEADER") <(grep "^${BackupFile}," $INFOFILE | tail -n $(( $(tput lines) -5 )) ) | column -ts,
exit 0
fi
_LOG DEBUG 最終バージョンファイルのチェック
LatestVersionFile="$( /bin/ls -1 | grep "^${BackupFile}" | grep -P '\.[0-9][0-9][0-9][0-9][0|1][0-9][1|2|3][0-9][a-z]*?\.bk$' | _sort | tail -1)"
_LOG DEBUG "最終バージョンファイル: $LatestVersionFile"
_LOG DEBUG 最終バージョンのチェック
LatestVersion="$(echo "$LatestVersionFile" | grep -oP '[0-9][0-9][0-9][0-9][0|1][0-9][1|2|3][0-9][a-z]*?\.bk$' |sed 's;\.bk;;g')"
_LOG DEBUG "最終バージョン: $LatestVersion"
_LOG DEBUG 最終バージョン日時のチェック
LatestVersionDate="$(echo "$LatestVersion" | cut -c 1-8 )"
_LOG DEBUG "最終バージョン日時: $LatestVersionDate"
_LOG DEBUG 最終バージョン文字のチェック
LatestVersionChar="$(echo "$LatestVersion" | cut -c 9- )"
_LOG DEBUG "最終バージョン文字: $LatestVersionChar"
_LOG DEBUG 最終バージョン文字の文字数のチェック
LatestVersionCharNum="$(echo -n "$LatestVersionChar" | wc -c )"
_LOG DEBUG "最終バージョン文字の文字数: $LatestVersionCharNum"
_LOG DEBUG 本日日時のチェック
Today=$(date +'%Y%m%d')
_LOG DEBUG "日付: $Today"
if [[ "$LatestVersionFile" == "" ]];then
_LOG INFO バックアップファイルが存在しませんでした。初期バックアップを実施します。
bk="${BackupFile}.${Today}a.bk"
_backup "${BackupFile}" "$bk" "${Today}a" "$(sha1sum "$BackupFile" | awk '{print $1}' )"
exit 0
fi
_LOG INFO "ハッシュ値照合中... [$LatestVersionFile]"
bkHash="$(sha1sum "$LatestVersionFile" | awk '{print $1}')"
nowHash="$(sha1sum "$BackupFile" | awk '{print $1}' )"
_LOG DEBUG "最終バックアップファイルのハッシュ値: $bkHash"
_LOG DEBUG "対象バックアップファイルのハッシュ値: $nowHash"
_LOG DEBUG ハッシュ比較
if [[ "$bkHash" == "$nowHash" ]];then
_LOG INFO バックアップファイルと同じハッシュのファイルなため、バックアップを実施しません。
exit 1
fi
_LOG DEBUG バックアップ日時を比較
if [[ "$Today" != "$LatestVersionDate" ]];then
_LOG INFO 本日最初のバックアップを実施します。
bk="${BackupFile}.${Today}a.bk"
_backup "${BackupFile}" "$bk" "${Today}a" "$nowHash"
exit 0
fi
_LOG DEBUG 最終バージョン文字の数よりバックアップ文字列を生成します。
CharVers=()
case ${LatestVersionCharNum} in
"1") CharVers=( $(echo {a..z}) );;
"2") CharVers=( $(echo {a..z}{a..z}) );;
"3") CharVers=( $(echo {a..z}{a..z}{a..z} ) );;
"4") CharVers=( $(echo {a..z}{a..z}{a..z}{a..z}) );;
*) _LOG ERROR "Error: NotSupportSubVersion: $LatestVersionChar"
esac
_LOG DEBUG 最終バージョン文字がその文字数における最後のバージョン文字かチェックします。
FirstFlag=0
case ${LatestVersionChar} in
"z") CharVers=( $(echo {a..z}{a..z}) ); FirstFlag=1;;
"zz") CharVers=( $(echo {a..z}{a..z}{a..z} ) ); FirstFlag=1;;
"zzz") CharVers=( $(echo {a..z}{a..z}{a..z}{a..z}) ); FirstFlag=1;;
esac
nextflag=0
NewVersionChar=""
if [[ $FirstFlag -eq 1 ]];then
_LOG DEBUG 最終バージョン文字がその文字数における最後のバージョン文字列だったため、文字数を拡張しました。
NewVersionChar="${CharVers[0]}"
else
_LOG DEBUG 最終バージョン文字がその文字数における最後のバージョン文字列ではないため、次の文字列を検索します。
for s in "${CharVers[@]}"
do
if [[ ${nextflag} -eq 1 ]];then
_LOG DEBUG "次のバージョン文字列を取得しました。[$s]"
NewVersionChar="$s"
break
elif [[ "$s" == "$LatestVersionChar" ]];then
_LOG DEBUG "現在のバージョン文字列と一致するものがありました。[$s]"
nextflag=1
continue
fi
done
fi
_LOG DEBUG 次のバージョン文字列が取得できたか
if [[ "$NewVersionChar" != "" ]];then
_LOG INFO 継続バックアップを開始します。
bk="${BackupFile}.${LatestVersionDate}${NewVersionChar}.bk"
_backup "${BackupFile}" "$bk" "${LatestVersionDate}${NewVersionChar}" "$nowHash"
fi


