個人的な備忘録まとめ：祝日データの自動取得と管理に関するスクリプト

## はじめに
このブログでは、祝日データを自動で取得し、管理するためのシェルスクリプトの実装方法について詳しく説明します。

具体的には、CSVファイルのバックアップ、ダウンロード、文字コード変換、ファイルサイズの検証、祝日の数の確認などの処理を行います。

これにより、祝日データの取得を自動化し、エラーが発生した場合には不要なファイルを削除する機能を追加することで、より堅牢なスクリプトに仕上げました。

## 前提条件

この記事は、個人の備忘録となっており、以下の関連記事の続きの記事となっています。

https://qiita.com/free-honda/items/bfd5233f6c46e265de6a

https://qiita.com/free-honda/items/2f07cda9fa77e63db248

### 追加処理：各処理が失敗したパターンで残ったファイルについて削除

```bash
#!/bin/bash

# 環境変数をセット
SYUKUJITSU_FILE="syukujitsu.csv"
BACKUP_FILE="backup_syukujitsu.csv"
NEW_FILE="new_syukujitsu.csv"
LOG_FILE="syukujitsu.log"
DOWNLOAD_URL="https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"

# 今ある祝日のCSVファイルをコピーする（バックアップファイルを作成する）
if cp "${SYUKUJITSU_FILE}" "${BACKUP_FILE}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップを作成しました。" > "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップの作成に失敗しました。" > "${LOG_FILE}"
    exit 1
fi

# CSVファイルを取得する
if curl -o "${NEW_FILE}" "${DOWNLOAD_URL}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 新しい祝日CSVファイルを取得しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 祝日CSVファイルの取得に失敗しました。" >> "${LOG_FILE}"
    if rm -f "${BACKUP_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

# 文字コードをSJISからUTF-8に変換する
if iconv -f SHIFT_JIS -t UTF-8 "${NEW_FILE}" > "${NEW_FILE}.utf8"; then
    mv "${NEW_FILE}.utf8" "${NEW_FILE}"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 文字コードの変換に成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 文字コードの変換に失敗しました。" >> "${LOG_FILE}"
    if rm -f "${BACKUP_FILE}" "${NEW_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルと新しいファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

# 新しいCSVファイルとバックアップファイルのサイズを比較
if [ "$(stat -c%s "${NEW_FILE}")" -ge "$(stat -c%s "${BACKUP_FILE}")" ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルサイズの検証に成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルサイズが小さくなっています。" >> "${LOG_FILE}"
    if rm -f "${BACKUP_FILE}" "${NEW_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルと新しいファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

# バックアップファイルを削除
if rm "${BACKUP_FILE}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップファイルを削除しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップファイルの削除に失敗しました。" >> "${LOG_FILE}"
    if rm -f "${BACKUP_FILE}" "${NEW_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルと新しいファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

# 新しいファイルをsyukujitsu.csvにリネーム
if mv "${NEW_FILE}" "${SYUKUJITSU_FILE}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルのリネームに成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルのリネームに失敗しました。" >> "${LOG_FILE}"
    if rm -f "${NEW_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要な新しいファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 新しいファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

echo "$(date '+%Y-%m-%d %H:%M:%S') - 処理が完了しました。" >> "${LOG_FILE}"
exit 0
```

### 追加処理：ファイルサイズが小さくなった場合

ファイルサイズが小さくなった場合に、今年の年を求め、new_syukujitsu.csvとbackup_syukujitsu.csvの中に今年の祝日の数を比較して、同じであれば正常、異なる場合は異常として処理します。

```bash
# 新しいCSVファイルとバックアップファイルのサイズを比較
if [ "$(stat -c%s "${NEW_FILE}")" -ge "$(stat -c%s "${BACKUP_FILE}")" ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルサイズの検証に成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルサイズが小さくなっています。" >> "${LOG_FILE}"

    # 現在の年を取得
    CURRENT_YEAR=$(date '+%Y')

    # NEW_FILEとBACKUP_FILEの祝日の数を比較
    NEW_FILE_HOLIDAYS=$(grep -c "${CURRENT_YEAR}" "${NEW_FILE}")
    BACKUP_FILE_HOLIDAYS=$(grep -c "${CURRENT_YEAR}" "${BACKUP_FILE}")

    if [ "${NEW_FILE_HOLIDAYS}" -eq "${BACKUP_FILE_HOLIDAYS}" ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 祝日の数が一致しました。正常とみなします。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 祝日の数が一致しません。異常とみなします。" >> "${LOG_FILE}"
        if rm -f "${BACKUP_FILE}" "${NEW_FILE}"; then
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルと新しいファイルを削除しました。" >> "${LOG_FILE}"
        else
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なファイルの削除に失敗しました。" >> "${LOG_FILE}"
        fi
        exit 1
    fi
fi
```

:::note
このスクリプトの変更点は以下の通りです。
年の取得: CURRENT_YEAR に現在の年を設定します。
祝日の数の比較: grep コマンドを用いて、それぞれのファイルに含まれる現在の年の行数（祝日の数）をカウントします。
祝日の数の一致を判定: NEW_FILE と BACKUP_FILE に含まれる祝日の数が一致する場合、正常と判断してそのまま進めます。異なる場合は、異常として不要なファイルを削除します。
:::

これにより、ファイルサイズが小さくなった場合でも、祝日のデータが正常であればそのまま処理を続行することが可能になります。

## 全体修正版コード

```bash
#!/bin/bash

# 環境変数をセット
SYUKUJITSU_FILE="syukujitsu.csv"
BACKUP_FILE="backup_syukujitsu.csv"
NEW_FILE="new_syukujitsu.csv"
LOG_FILE="syukujitsu.log"
DOWNLOAD_URL="https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"

# 今ある祝日のCSVファイルをコピーする（バックアップファイルを作成する）
if cp "${SYUKUJITSU_FILE}" "${BACKUP_FILE}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップを作成しました。" > "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップの作成に失敗しました。" > "${LOG_FILE}"
    exit 1
fi

# CSVファイルを取得する
if curl -o "${NEW_FILE}" "${DOWNLOAD_URL}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 新しい祝日CSVファイルを取得しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 祝日CSVファイルの取得に失敗しました。" >> "${LOG_FILE}"
    if rm -f "${BACKUP_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

# 文字コードをSJISからUTF-8に変換する
if iconv -f SHIFT_JIS -t UTF-8 "${NEW_FILE}" > "${NEW_FILE}.utf8"; then
    mv "${NEW_FILE}.utf8" "${NEW_FILE}"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 文字コードの変換に成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 文字コードの変換に失敗しました。" >> "${LOG_FILE}"
    if rm -f "${BACKUP_FILE}" "${NEW_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルと新しいファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

# 新しいCSVファイルとバックアップファイルのサイズを比較
if [ "$(stat -c%s "${NEW_FILE}")" -ge "$(stat -c%s "${BACKUP_FILE}")" ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルサイズの検証に成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルサイズが小さくなっています。" >> "${LOG_FILE}"

    # 現在の年を取得
    CURRENT_YEAR=$(date '+%Y')

    # NEW_FILEとBACKUP_FILEの祝日の数を比較
    NEW_FILE_HOLIDAYS=$(grep -c "${CURRENT_YEAR}" "${NEW_FILE}")
    BACKUP_FILE_HOLIDAYS=$(grep -c "${CURRENT_YEAR}" "${BACKUP_FILE}")

    if [ "${NEW_FILE_HOLIDAYS}" -eq "${BACKUP_FILE_HOLIDAYS}" ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 祝日の数が一致しました。正常とみなします。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 祝日の数が一致しません。異常とみなします。" >> "${LOG_FILE}"
        if rm -f "${BACKUP_FILE}" "${NEW_FILE}"; then
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なバックアップファイルと新しいファイルを削除しました。" >> "${LOG_FILE}"
        else
            echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要なファイルの削除に失敗しました。" >> "${LOG_FILE}"
        fi
        exit 1
    fi
fi

# バックアップファイルを削除
if rm "${BACKUP_FILE}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップファイルを削除しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - バックアップファイルの削除に失敗しました。" >> "${LOG_FILE}"
    exit 1
fi

# 新しいファイルをsyukujitsu.csvにリネーム
if mv "${NEW_FILE}" "${SYUKUJITSU_FILE}"; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルのリネームに成功しました。" >> "${LOG_FILE}"
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ファイルのリネームに失敗しました。" >> "${LOG_FILE}"
    if rm -f "${NEW_FILE}"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 不要な新しいファイルを削除しました。" >> "${LOG_FILE}"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - 新しいファイルの削除に失敗しました。" >> "${LOG_FILE}"
    fi
    exit 1
fi

echo "$(date '+%Y-%m-%d %H:%M:%S') - 処理が完了しました。" >> "${LOG_FILE}"
exit 0
```

## フロー処理の流れ

```css
1. 環境変数のセット
   |
   V
2. 祝日CSVファイルのバックアップ
   | 
   |-- 成功 → ログに「バックアップを作成しました。」と記録 → 次のステップへ
   |
   |-- 失敗 → ログに「バックアップの作成に失敗しました。」と記録 → スクリプト終了

3. 新しい祝日CSVファイルのダウンロード
   |
   |-- 成功 → ログに「新しい祝日CSVファイルを取得しました。」と記録 → 次のステップへ
   |
   |-- 失敗 → ログに「祝日CSVファイルの取得に失敗しました。」と記録
   |             |
   |             |-- バックアップファイルを削除 → ログに「不要なバックアップファイルを削除しました。」と記録
   |             |
   |             |-- スクリプト終了

4. 文字コードの変換
   |
   |-- 成功 → ログに「文字コードの変換に成功しました。」と記録 → 次のステップへ
   |
   |-- 失敗 → ログに「文字コードの変換に失敗しました。」と記録
   |             |
   |             |-- 不要なバックアップファイルと新しいファイルを削除 → ログに記録
   |             |
   |             |-- スクリプト終了

5. 新しいCSVファイルとバックアップファイルのサイズを比較
   |
   |-- 成功 → ログに「ファイルサイズの検証に成功しました。」と記録 → 次のステップへ
   |
   |-- 失敗 → ログに「ファイルサイズが小さくなっています。」と記録
   |             |
   |             |-- 現在の年を取得
   |             |
   |             |-- NEW_FILEとBACKUP_FILEの祝日の数を比較
   |             |
   |             |-- 一致 → ログに「祝日の数が一致しました。正常とみなします。」と記録 → 次のステップへ
   |             |
   |             |-- 不一致 → ログに「祝日の数が一致しません。異常とみなします。」と記録
   |                           |
   |                           |-- 不要なバックアップファイルと新しいファイルを削除 → ログに記録
   |                           |
   |                           |-- スクリプト終了

6. バックアップファイルの削除
   |
   |-- 成功 → ログに「バックアップファイルを削除しました。」と記録 → 次のステップへ
   |
   |-- 失敗 → ログに「バックアップファイルの削除に失敗しました。」と記録 → スクリプト終了

7. 新しいファイルをsyukujitsu.csvにリネーム
   |
   |-- 成功 → ログに「ファイルのリネームに成功しました。」と記録
   |
   |-- 失敗 → ログに「ファイルのリネームに失敗しました。」と記録
   |             |
   |             |-- 不要な新しいファイルを削除 → ログに記録
   |             |
   |             |-- スクリプト終了

8. 処理が完了しました。とログに記録
```

## まとめ

このスクリプトを使用することで、祝日データを自動で管理し、万が一のエラーにも対応できるようになります。今後も、スクリプトの改善や新しい機能の追加を行っていく予定です。ぜひ、実際に試してみてください。

## おまけ：個人メモです。

syukujitsu.csvが存在するかどうかを判定し、存在しない場合は代替ファイルから祝日データを取得する処理を実装しています。

```bash
#!/bin/bash

# 環境変数をセット
HOLIDAYS_FILE="$HOME/syukujitsu.csv"
ALTERNATE_FILE="/path/to/alternate/syukujitsu.csv"  # 代替ファイルのパスを指定

# syukujitsu.csvの存在を確認
if [ ! -f "$HOLIDAYS_FILE" ]; then
    echo "$HOLIDAYS_FILE が見つかりません。代替ファイルを使用します。"
    HOLIDAYS_FILE="$ALTERNATE_FILE"  # 代替ファイルを使用
fi

# 今日の日付を取得
TODAY=$(date +%Y/%-m/%-d)

# 祝日判定
if grep -q "^$TODAY," "$HOLIDAYS_FILE"; then
    holiday="holiday"
else
    holiday=""
fi

# 結果の表示
if [ -n "$holiday" ]; then
    echo "$TODAY は祝日です。"
else
    echo "$TODAY は祝日ではありません。"
fi
```

### スクリプトの処理内容
**環境変数のセット:**
HOLIDAYS_FILE に祝日データが保存されているファイルのパスを指定。
ALTERNATE_FILE に代替ファイルのパスを指定。

**ファイルの存在確認:**
syukujitsu.csv が存在しない場合、ALTERNATE_FILE を使用する旨のメッセージを表示し、HOLIDAYS_FILE を代替ファイルに設定。

**今日の日付の取得:**
TODAY に現在の日付を「YYYY/MM/DD」の形式で格納。

**祝日判定:**
grep コマンドを使用して、HOLIDAYS_FILE に今日の日付が存在するかをチェックし、祝日の場合は変数 holiday に "holiday" をセット。