日本の祝日を自動取得して活用するシェルスクリプト検証

## はじめに

実務で祝日ファイルを自動取得し、祝日判定を行う案件に取り組んだ際、祝日情報をシステムにどのように取り込み、スムーズに反映させるかが全く分からず、苦労した経験があります。

このブログ記事は、その経験から得た知見をまとめた忘備録になります。

例えば、バックアップやサーバーメンテナンスのタイミングを平日や特定の祝日に合わせて調整する必要がある場合、シェルスクリプトを用いて祝日情報を自動取得し、処理に反映させることが可能です。

今回は、日本の祝日を自動的に取得し、ファイルに保存するシェルスクリプトをご紹介します。このスクリプトを活用することで、業務の自動化やスケジュール調整がより効率的に行えるようになります。

## 前提条件
今回の検証を進めるにあたり、以下の環境が必要です。

:::note info
**検証環境**
**Linuxディストリビューション:**
仮想環境や実際のサーバー上でLinux（ここではAlmaLinux 9.3を使用）が稼働していること。

**インターネット接続:** 
国民の祝日のCSVファイルを取得するため、インターネットに接続できること。
:::

**自宅のVirtualBox環境**
今回も VirtualBox 上で構築した検証環境を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/70df8950-ddfa-2728-1d71-60373a8276e9.png)


:::note info
※検証には実行中の「AlmaLinux」を使用します。
:::

## 知識整理
今回の構築に必要な知識を簡単に整理します。

**シェルスクリプト**
LinuxやUnixシステム上で複数のコマンドを自動的に実行するためのプログラムです。

繰り返し行う作業や、特定の条件に応じた処理を自動化するのに役立ち、特にシステム管理や業務の効率化に使われます。
簡単なテキストファイル形式で作成でき、bashやshなどのシェル環境で実行されます。


:::note warn
前回の記事でもシェルスクリプトを使った検証をしているので、興味のある方は読んでみてください。
[Linuxでの定期再起動をシェルスクリプトとcronジョブで自動化する方法と検証結果](https://qiita.com/free-honda/items/efa3c889888fd7ef75b5)

さらに詳しく知りたい場合は、以下のリンクも参考にしてください。
**シェルスクリプトについて**
参考サイト：https://sc.megabank.tohoku.ac.jp/ph3-doc/howto/linux/script.html
参考サイト：https://jitera.com/ja/insights/25777
:::

## 構築の流れ
ここでは、検証用のLinuxサーバーを使い、シェルスクリプトの作成と実行手順について2つのステップに分けて解説します。

:::note info
シェルスクリプトの概要と作成及び実行手順については、以下になります。
**前提：スクリプトのフロー図とポイント**
**ステップ1：スクリプトの作成**
**ステップ2：スクリプトの実行確認**
:::

今回の検証におけるディレクトリ構成は以下の通りです。
```
.
├── holiday.sh      # 祝日取得用のシェルスクリプト
├── holiday.txt     # 取得した祝日情報が保存されるファイル
└── holiday.log     # 実行結果やログ情報が記録されるファイル
```

## スクリプトのフロー図とポイント

以下に「holiday.sh」のフローを簡単にまとめたフローチャートを示します。


```css
+--------------------------+
|      スクリプト開始      |
+--------------------------+
              |
              v
+--------------------------+
| 今年の年を取得 (`this_year=$(date +%Y)`) |
+--------------------------+
              |
              v
+--------------------------+
| holiday.txt ファイルをクリア |
+--------------------------+
              |
              v
+--------------------------+
| CSVファイルをダウンロード (curl) |
+--------------------------+
              |
              v
+--------------------------+
| CSVファイルをUTF-8に変換 (iconv) |
+--------------------------+
              |
              v
+--------------------------+
| 各行を処理                |
| - 今年の祝日か判定        |
|   - YES → 日付をフォーマットして holiday.txt に保存 |
+--------------------------+
              |
              v
+--------------------------+
| ログに祝日取得完了を記録 |
+--------------------------+
              |
              v
+--------------------------+
| 終了ログを記録 (`"正常終了: $(date)"`) |
+--------------------------+
              |
              v
+--------------------------+
|         スクリプト終了     |
+--------------------------+

```

このスクリプトのポイントは主に3つです。

```
⓵祝日の取得
内閣府の祝日CSVファイルをダウンロードし、現在の年に該当する祝日を抽出しています。

⓶SHIFT_JISからUTF-8への変換
内閣府の祝日CSVファイルはSHIFT_JISエンコードなので、iconvコマンドを使ってUTF-8に変換しています。

⓷ファイルへの出力
当年の祝日をholiday.txtに出力し、また処理が完了したことをholiday.logに記録しています。

```

## ステップ1：スクリプトの作成
前提として、検証環境がすでに構築されていることを想定しています。

以下の手順で、「holiday.sh」という名前のスクリプトを作成し、祝日情報を取得する準備をします。

ターミナルを開き、以下のコマンドで新しいファイルを作成します。
```
vi holiday.sh
```

エディタを開いたら、以下の内容をファイルに貼り付けます。※ログの出力先などは、ご自身の環境に合わせて修正してください。

```holiday.sh
#!/bin/bash

# 今年の年を取得する
this_year=$(date +%Y)

# holiday.txtファイルをクリアする
> holiday.txt

# CSVファイルをダウンロードするURL
url="https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"

# CSVファイルをダウンロードしてholiday.txtに変換して保存する
curl -s $url | iconv -f SHIFT_JIS -t UTF-8 | tail -n +2 | while IFS=, read -r date name; do
    if [[ $date =~ ^$this_year ]]; then
        echo "${date//\//-}" >> holiday.txt
    fi
done

# 祝日を取得して出力する
echo "現在の祝日の取得完了" >> /home/honda/holiday/holiday.log
echo "正常終了: $(date '+%Y-%m-%d %H:%M:%S')" >> /home/honda/holiday/holiday.log
```

ファイルを保存し、エディタを閉じます。これでスクリプトの作成は完了です。

## ステップ2：スクリプトの実行確認

作成したスクリプトを実行するには、まず実行権限を付与し、実行コマンドを実行します。
スクリプトに実行権限を付与し、以下のコマンドでスクリプトを実行します。

```
./holiday.sh
```

スクリプトが正常に動作すると、holiday.txtファイルにその年の祝日が日付形式で保存され、ログは/home/honda/holiday.logに記録されます。

**holiday.txtファイルの中身**
holiday.txtには、当年の祝日が以下のように日付形式で保存されています。

```
[root@HONDA-TEST holiday]# cat holiday.txt
2024-1-1
2024-1-8
2024-2-11
・・・
[root@HONDA-TEST holiday]#
```

**holiday.logファイルの中身**
スクリプトの実行結果は、holiday.logに記録されます。

```
[root@HONDA-TEST holiday]# cat holiday.log
現在の祝日の取得完了
正常終了: 2024-09-16 16:13:55
[root@HONDA-TEST holiday]#
```

両方のファイルも想定通りの出力結果が得られていることを確認できましたので、自宅での検証も完了です。

## まとめ

今回紹介したシェルスクリプトを使えば、最新の日本の祝日情報を自動取得し、スケジュール管理や業務の自動化に役立てることができます。

祝日データを活用することで、バックアップやメンテナンスのスケジュール調整、プロモーション活動の自動化、またはサービス提供時間の調整など、さまざまな用途に応用できます。

次回は、このシェルスクリプトをさらに発展させ、他のシェルスクリプト内で祝日判定のロジックを実装する高度な検証を行いたいと考えています。お楽しみに！

## おまけ①
上記のスクリプトは、毎年最新の祝日データを取得するのに役立ちますが、cronなどを利用して定期的に実行することで、常に最新の祝日情報を保つことも可能です。

以下のように設定すれば、1年に一度、年始に自動的に祝日データを更新するタスクを組むことができます。

```
crontab -e

**入力内容**
・・・・・
0 0 1 * * /home/honda/holiday/holiday.sh
```

この設定により、毎年1月1日に自動的に祝日データが更新されます。

## 自分用メモ
取得が成功した場合は「syukujitsu.csv」を保存し、失敗した場合は「old_syukujitsu.csv」を使用します。

**自分メモ**
```bash
#!/bin/bash

# CSVファイルの取得先URL
URL="https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"

# 取得したCSVファイルの保存先パス（/rootディレクトリ）
SAVE_PATH="/home/honda/syukujitsu.csv"

# 既存のCSVファイルのパス
EXISTING_CSV="/home/honda/old_syukujitsu.csv"

# ログファイルのパス
LOG_FILE="/home/honda/syukujitsu.log"

# CSVファイルを取得する関数
fetch_csv() {
    # curlを使用してURLからファイルをダウンロード
    curl -sSfL "$URL" -o "$SAVE_PATH"
}

# CSVファイルのバックアップを作成
cp "$SAVE_PATH" "$EXISTING_CSV"

# CSVファイルを取得して保存
fetch_csv

# 取得に失敗した場合は古いCSVファイルを使用
if [ $? -ne 0 ]; then
    echo "Failed to fetch CSV file from $URL." >> "$LOG_FILE"
    # 失敗した場合は「old_syukujitsu.csv」として保存
    mv "$SAVE_PATH" "$EXISTING_CSV"
else
    # 文字コードをLinux用に変換（UTF-8に変換）
    iconv -f SJIS -t UTF-8 "$SAVE_PATH" > "$SAVE_PATH.tmp" && mv "$SAVE_PATH.tmp" "$SAVE_PATH"
    # 既存のCSVファイルを削除
    rm -f "$EXISTING_CSV"
fi
```

**自分メモ**
```bash
#!/bin/bash

# 祝日ファイルのパスを変数にセット
HOLIDAY_FILE="/root/syukujitsu.csv"           # 現在の祝日ファイルのパス
OLD_HOLIDAY_FILE="/root/old_syukujitsu.csv"   # バックアップ祝日ファイルのパス

# メイン処理
while [ $FLG_1 -eq 0 ]   # フラグが0の間、ループを続ける
do				
    # 祝日ファイルの存在確認
    if [ -f "$HOLIDAY_FILE" ]; then  # 現在の祝日ファイルが存在するか確認
        CURRENT_HOLIDAY_FILE="$HOLIDAY_FILE"  # 存在すれば、そのファイルを使用
    else
        CURRENT_HOLIDAY_FILE="$OLD_HOLIDAY_FILE"  # 存在しなければ、バックアップファイルを使用
    fi

    # ここで CURRENT_HOLIDAY_FILE を使用して処理を続ける
    # 例: cat "$CURRENT_HOLIDAY_FILE"  # 現在の祝日ファイルを表示するコマンド（例）

    ### その他の処理をここに追加...
done
```

## 参考記事
https://qiita.com/mindwood/items/d22689d3da61034751bd

https://qiita.com/curry_rohio/items/31263850f847ad475931

https://www.seeds-std.co.jp/blog/creators/2023-01-17-131624/