はじめに
bashシェルスクリプトの勉強がてらに、実環境でも使えそうなスクリプトの作成をしてみようと思い、今回のログバックアップスクリプトの作成を行いました。
まだ実務4カ月程の為お手柔らかにお願いします。
スクリプトの要件
- バックアップ先ディレクトリは自動作成
- ログローテートされた前日日付のログをバックアップ
- gzip形式で圧縮
- 圧縮前の生データはそのまま残す
- backupディレクトリ内で各ログのディレクトリを振り分ける
- backupディレクトリは/tmp/配下に(テストなんで適当に)
- 7日以上前のバックアップは削除する
- 各処理をログに出力
- ログにはタイムスタンプを付ける
- 削除したログは","区切りで分かりやすく表示
- 失敗処理には [error] タグをつける
- コマンドの標準エラー出力はログに出力
- バックアップ対象はリスト化しループ処理させる
ざっくり以上の要件で作成を行いました。
スクリプト本文
#!/bin/bash
yesterday=`date -d "1 days ago" "+%Y%m%d"`
backup_log="/var/log/backup.log"
list=(
"/var/log/messages"
"/var/log/cron"
)
for i in ${list[@]}
do
# listから第4フィールドを抜き出し変数へ格納
log_name=`echo "${i}" | awk -F '/' '{print $4}'`
# バックアップ対象のファイル名
log="${i}"-"${yesterday}"
# 圧縮後のログファイル名
commpress_log="${log}.gz"
# バックアップの転送先ディレクトリ
to_dir="/tmp/backup/${log_name}/"
# バックアップ転送先ディレクトリが存在しなければ作成
if [[ ! -d "${to_dir}" ]]; then
mkdir -p "${to_dir}" 2>> "${backup_log}"
fi
# 処理開始
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] ログバックアップを開始します" > /dev/null >> "${backup_log}"
# バックアップ対象の存在確認、存在していればバックアップ処理
if [[ -f "${log}" ]];then
gzip -fk "${log}" 2>> "${backup_log}"
rc1=$?
mv "${commpress_log}" "${to_dir}" 2>> "${backup_log}"
rc2=$?
# 圧縮、転送が完了していれば成功
if [[ "${rc1}" == 0 && "${rc2}" == 0 ]]; then
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] "${log}"のバックアップが成功しました" > /dev/null >> "${backup_log}"
else
echo "`date "+%Y/%m/%d %H:%M:%S"`: [error] "${log}"のバックアップが失敗しました" > /dev/null >> "${backup_log}"
fi
else
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] バックアップ対象のログが存在しませんでした" > /dev/null >> "${backup_log}"
fi
# 保管期間の過ぎたバックアップの削除処理を開始
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] 保存期間切れの "${log_name}" の削除を開始します" > /dev/null >> "${backup_log}"
# 7日以上の前のバックアップが存在すれば変数に格納
searchlog=`find "${to_dir}" -type f -mtime +6 -name ""${log_name}"-*"`
# 保管期間切れのバックアップのファイル名だけを抽出、複数あれば","区切りで表示
rmlog=`echo "${searchlog}" | awk -F '/' '{print $5}' | sed -z 's/\n/,/g'`
# 7日以上の前のバックアップが存在すれば削除
if [[ ! -z "${searchlog}" ]]; then
echo "${searchlog}" | xargs rm -f 2>> "${backup_log}"
rc3=$?
if [[ "${rc3}" == 0 ]]; then
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] 削除ファイル: "${rmlog}"" > /dev/null >> "${backup_log}"
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] 保管期間切れ "${log_name}" の削除が成功しました " > /dev/null >> "${backup_log}"
else
echo "`date "+%Y/%m/%d %H:%M:%S"`: [error] 保管期間切れ "${log_name}" の削除が失敗しました " > /dev/null >> "${backup_log}"
fi
else
echo "`date "+%Y/%m/%d %H:%M:%S"`: [info] 保存期間切れの "${log_name}" は存在しませんでした" > /dev/null >> "${backup_log}"
fi
done
実行してみた
前提
- スクリプトファイル名"test.sh"
- 格納先/home/test/
バックアップ対象となるログを1日前の日付で適当に作成します。
今回はmessagesとcronを対象にします。
$ touch -t "202404050000" /var/log/messages-20240405
$ touch -t "202404050000" /var/log/cron-20240405
ではスクリプトを実行してみます。
$ /home/test/test.sh
$ pwd
/tmp/backup
$ ll
合計 0
drwxr-xr-x. 2 root root 30 4月 6 20:30 cron
drwxr-xr-x. 2 root root 34 4月 6 20:30 messages
/tmp/backupは以下に
- corn
- messages
ディレクトリが作成されています。
それぞれ中を確認してみます。
$ pwd
/tmp/backup/cron
$ ll
合計 4
-rw-r--r--. 1 root root 34 4月 5 00:00 cron-20240405.gz
$ pwd
/tmp/backup/messages
$ ll
合計 4
-rw-r--r--. 1 root root 38 4月 5 00:00 messages-20240405.gz
無事バックアップ成功しているようですね。
では、ログの出力を確認してみます。
# cat /var/log/backup.log
2024/04/06 20:30:37: [info] ログバックアップを開始します
2024/04/06 20:30:37: [info] /var/log/messages-20240405のバックアップが成功しました
2024/04/06 20:30:37: [info] 保存期間切れの messages の削除を開始します
2024/04/06 20:30:37: [info] 保存期間切れの messages は存在しませんでした
2024/04/06 20:30:37: [info] ログバックアップを開始します
2024/04/06 20:30:37: [info] /var/log/cron-20240405のバックアップが成功しました
2024/04/06 20:30:37: [info] 保存期間切れの cron の削除を開始します
2024/04/06 20:30:37: [info] 保存期間切れの cron は存在しませんでした
このようにログも問題なく出力されていますが。
続いては、削除対象のログを適当に作成します。
複数対象が欲しいのでそれぞれ2ファイルずつ7日以上前の日付で作成します。
$ touch -t "202403010000" /tmp/backup/messages/messages-202403
01.gz
$ touch -t "202403020000" /tmp/backup/messages/messages-202403
02.gz
$ touch -t "202403010000" /tmp/backup/cron/cron-20240301.gz
$ touch -t "202403020000" /tmp/backup/cron/cron-20240302.gz
それぞれのディレクトリ内に作成されたことを確認します。
$ pwd
/tmp/backup/messages
$ ll
合計 4
-rw-r--r--. 1 root root 0 3月 1 00:00 messages-20240301.gz
-rw-r--r--. 1 root root 0 3月 2 00:00 messages-20240302.gz
-rw-r--r--. 1 root root 38 4月 5 00:00 messages-20240405.gz
$ pwd
/tmp/backup/cron
$ ll
合計 4
-rw-r--r--. 1 root root 0 3月 1 00:00 cron-20240301.gz
-rw-r--r--. 1 root root 0 3月 2 00:00 cron-20240302.gz
-rw-r--r--. 1 root root 34 4月 5 00:00 cron-20240405.gz
再びスクリプトを実行し、ログの出力を確認してみます。
2024/04/06 20:39:55: [info] ログバックアップを開始します
2024/04/06 20:39:55: [info] /var/log/messages-20240405のバックアップが成功しました
2024/04/06 20:39:55: [info] 保存期間切れの messages の削除を開始します
2024/04/06 20:39:55: [info] 削除ファイル: messages-20240301.gz,messages-20240302.gz
2024/04/06 20:39:55: [info] 保管期間切れ messages の削除が成功しました
2024/04/06 20:39:55: [info] ログバックアップを開始します
2024/04/06 20:39:55: [info] /var/log/cron-20240405のバックアップが成功しました
2024/04/06 20:39:55: [info] 保存期間切れの cron の削除を開始します
2024/04/06 20:39:55: [info] 削除ファイル: cron-20240301.gz,cron-20240302.gz
2024/04/06 20:39:55: [info] 保管期間切れ cron の削除が成功しました
それぞれ
- /tmp/backup/messages
- /tmp/backup/cron
の中身を確認してみます。
$ pwd
/tmp/backup/messages
$ ll
合計 4
-rw-r--r--. 1 root root 38 4月 5 00:00 messages-20240405.gz
$ pwd
/tmp/backup/cron
$ ll
合計 4
-rw-r--r--. 1 root root 34 4月 5 00:00 cron-20240405.gz
このように保管期間の過ぎたファイルの削除も問題なく行われていますね。
エラーを起こしてみた
以下の通りわざと存在しないコマンドに置き換えます。
# バックアップ対象の存在確認、存在していればバックアップ処理
if [[ -f "${log}" ]];then
# Agzipという存在しないコマンドにしてみます
Agzip -fk "${log}" 2>> "${backup_log}"
rc1=$?
では、実行してログを確認してみます。
2024/04/06 20:48:56: [info] ログバックアップを開始します
/home/test/test.sh: 行 37: Agzip: コマンドが見つかりません
mv: '/var/log/messages-20240405.gz' を stat できません: そのようなファイルやディレクトリはありません
2024/04/06 20:48:56: [error] /var/log/messages-20240405のバックアップが失敗しま した
2024/04/06 20:48:56: [info] 保存期間切れの messages の削除を開始します
2024/04/06 20:48:56: [info] 保存期間切れの messages は存在しませんでした
2024/04/06 20:48:56: [info] ログバックアップを開始します
/home/test/test.sh: 行 37: Agzip: コマンドが見つかりません
mv: '/var/log/cron-20240405.gz' を stat できません: そのようなファイルやディレクトリはありません
2024/04/06 20:48:56: [error] /var/log/cron-20240405のバックアップが失敗しました
2024/04/06 20:48:56: [info] 保存期間切れの cron の削除を開始します
2024/04/06 20:48:56: [info] 保存期間切れの cron は存在しませんでした
このように、失敗判定と標準エラー出力がログに書き込まれていますね。
最後に
まだまだ初学者ということもありスクリプトの記述に無駄や改善点が多くあるかもしれません。
何かアドバイス等あれば是非、コメントで教えていただけると幸いです。