【bash】bashシェルスクリプトでログのバックアップ【Linux】

# はじめに
bashシェルスクリプトの勉強がてらに、実環境でも使えそうなスクリプトの作成をしてみようと思い、今回のログバックアップスクリプトの作成を行いました。

まだ実務4カ月程の為お手柔らかにお願いします。

# スクリプトの要件
- バックアップ先ディレクトリは自動作成
- ログローテートされた前日日付のログをバックアップ
- gzip形式で圧縮
- 圧縮前の生データはそのまま残す
- backupディレクトリ内で各ログのディレクトリを振り分ける
- backupディレクトリは/tmp/配下に（テストなんで適当に）
- 7日以上前のバックアップは削除する
- 各処理をログに出力
- ログにはタイムスタンプを付ける
- 削除したログは","区切りで分かりやすく表示
- 失敗処理には **[error]** タグをつける
- コマンドの標準エラー出力はログに出力
- バックアップ対象はリスト化しループ処理させる

ざっくり以上の要件で作成を行いました。

# スクリプト本文

```bash
#!/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から第４フィールドを抜き出し変数へ格納
	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}"	
	# ７日以上の前のバックアップが存在すれば変数に格納
	searchlog=`find "${to_dir}" -type f -mtime +6 -name ""${log_name}"-*"`
	# 保管期間切れのバックアップのファイル名だけを抽出、複数あれば","区切りで表示
	rmlog=`echo "${searchlog}" | awk -F '/' '{print $5}' | sed -z 's/\n/,/g'`
	
	# ７日以上の前のバックアップが存在すれば削除
	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を対象にします。

```shell-session
$ touch -t "202404050000" /var/log/messages-20240405
$ touch -t "202404050000" /var/log/cron-20240405
```

ではスクリプトを実行してみます。
```shell-session
$ /home/test/test.sh
```

```shell-session
$ 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
ディレクトリが作成されています。
それぞれ中を確認してみます。
```shell-session
$ 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
```

無事バックアップ成功しているようですね。

では、ログの出力を確認してみます。
```shell-session
# 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日以上前の日付で作成します。

```shell-session
$ 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
```
それぞれのディレクトリ内に作成されたことを確認します。
```shell-session
$ 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
```

再びスクリプトを実行し、ログの出力を確認してみます。
```shell-session
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
の中身を確認してみます。
```shell-session
$ 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
```

このように保管期間の過ぎたファイルの削除も問題なく行われていますね。

# エラーを起こしてみた

以下の通りわざと存在しないコマンドに置き換えます。
```bash
# バックアップ対象の存在確認、存在していればバックアップ処理
	if [[ -f "${log}" ]];then
		# Agzipという存在しないコマンドにしてみます
		Agzip -fk "${log}" 2>> "${backup_log}"
		rc1=$?
```

では、実行してログを確認してみます。
```shell-session
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 は存在しませんでした
```
このように、失敗判定と標準エラー出力がログに書き込まれていますね。

# 最後に
まだまだ初学者ということもありスクリプトの記述に無駄や改善点が多くあるかもしれません。
何かアドバイス等あれば是非、コメントで教えていただけると幸いです。