【PowerShell】フラグ連携用のファイル監視を行う関数を作成する

# フラグ連携とは

フラグとは端的にいうと後続の処理を制御する際の**判定値**ないしはそれを保存しておく**領域**のことを指します。

プログラム内ならこの領域が変数にあたり、変数の中にブール値を格納してそれを判定し処理を分岐させたりしますが、ファイルの存在有無を基にしてTrue/Falseを判定しようというのがフラグファイル連携となります。
つまるところ、「フラグファイル」とは特殊なファイルではなく、条件判定を行うために指定されたただのファイルです。

フラグはそのまま英語の「旗」を意味する"Flag"から来ているため、
イメージとしては2つの〇×の旗を持った人が立っている感じと勝手に思っています。

フラグは"FLG"と表記されることもあります。
また「フラッグ」と呼ぶこともあります。

つまり先の話をまとめるとこうです。

- 領域：ディレクトリ
- 判定値：指定ファイルの存在有無

## どこで使われるのか?
主にサーバー運用でのバッチ処理で用いられます。

複数のサーバを跨いでの連携などにおいて、
特定の処理の開始終了をフラグの有無で判定します。

例えば以下のようなフローで動作させる必要があるとします。
1. サーバＡがバッチ処理を開始する
1. サーバＢはサーバAのバッチ処理が完了した後に特定のバッチ処理を動作させる

この場合、サーバBはサーバAのバッチ処理の完了を判定する必要がありますね。
こういったケースの解決策のひとつとしてフラグ連携を考えます。

# フラグファイル監視関数
今回はフラグファイル監視用の関数を作成します。

先の連携の例に当てはめるとするとこんな感じです。

1. サーバAがバッチ処理を開始する
1. サーバAはバッチ処理完了時にサーバBの特定フォルダへフラグファイルを送る。
1. サーバBはフラグファイルを検知し自身のバッチ処理を開始する。

今回作成する関数はこの3番目のフローにおいて、
フラグファイルを検知する仕組みを提供するものです。

## ソースコード
```powershell:Start-FlgCheck
#フラグ監視ツール
function Start-FlgCheck () {
    Param (
        [Parameter(Mandatory=$true)][int]$limitTime,
        [Parameter(Mandatory=$true)][int]$interval,
        [Parameter(Mandatory=$true)][string]$targetFile
    )

        do {
            if((Test-Path $targetFile -PathType Leaf)){
                return $true
            }
            Write-Host "ファイルがみつかりませんでした。`n${interval}秒待機します..."
            Start-Sleep -Seconds $interval
            $limitTime -= $interval
            #Write-Host $limitTime
        } while ($limitTime -gt 0 )

        return $false

}
```
## 書式
```powershell
Start-FlgCheck -limitTime <int> -interval <int> -targetFile <string>
```

## パラメータ一覧
| パラメーター | 説明                         | 
| :----------- | ---------------------------- | 
| -limitTime   | 監視時間の上限値             | 
| -interval    | フラグファイルのチェック間隔 | 
| -targerFile  | フラグファイルのパス         | 

パラメータの値は全て必須項目となっているので注意してください。

## 解説
今回はループ処理にdo-whileを採用しました。
理由は以下です。
- ファイルチェックは最低でも1回は実行される必要があるため(0回のパターンはあり得ない)
- 初期化式が不要なため(for文に拘る必要なし)

whileやforを使用すると必ず1回はループの条件判定が走ります。
処理の遅延などがない限り、関数実行時に既にファイルが置かれているといったケースでは無駄な条件判定を挟むことになるため、条件判定を後ろに記述するdo-whileが適していると判断しました。

しかしfor文は初期化式、判定式、更新式を一度にまとめて記述できるため、
コードのわかりやすさに貢献すると思います。
なので可読性の面からfor文の採用はまだ選択肢としてはありです。
ただし、今回は``$limitTime``の値を``$interval``で逐次減算していく仕様にすることで初期化式が不要になりました。
ということで、do-whileが一番適しているだろうという判断となりました。

### 先の連携例に当てはめる

>1. サーバAがバッチ処理を開始する
>1. サーバAはバッチ処理完了時にサーバBの特定フォルダへフラグファイルを送る。
>1. サーバBはフラグファイルを検知し自身のバッチ処理を開始する。

関数の``$targetFile``の値にサーバAから送られてくるフラグファイルを格納するディレクトリとフラグファイル名をセットします。

サーバBはタスクスケジューラやcronなどスケジューラソフトを使用して、
サーバAからフラグ連携のタイミングでこの処理を走らせます。

ファイルが格納されると関数は``$true``を返すため、
その判定値を基に後続の処理を動かします。
``$false``が返ってきた場合、サーバAのバッチ処理が正常終了していない可能性があるため、
後続の処理が動かないようするなど、エラーハンドリングを行います。

## 注意点
この関数の注意点というよりは、フラグファイル連携の注意となりますが、
フラグファイルはファイル内に中身のない空ファイル(0バイトファイル)が望ましいです。

例えばDBから任意のテーブル情報を抽出しCSVファイルの形で特定のディレクトリに格納したとします。
このファイルをトリガーとし、後続処理を走らせようとしたときに対象のファイルへの書き込みが全て完了していない状態であったとしても、
ファイルが作成された時点でTrue判定となるからです。
このように作成と書き込み完了までの間が大きくなる場合に注意が必要になります。

このような意図しない動作を防ぐには先に述べたようにフラグ連携用のファイルは空ファイルを使うようにルール決めすることです。
先のDBの例の場合は以下のようなフローにすることで防ぐことができます。
1. DBからテーブル情報を抽出開始
1. フラグファイル監視開始
1. 抽出完了→フラグファイル発行
1. ファイル検知OK→後続処理へ


## 余談
今回は監視時間のリミットを設けましたが、
無限ループにしてバックグランドで走らせることで、常に特定のディレクトリ配下のファイル監視を行うといったデーモンのようなプログラムとして動かすのもありだと思います。

またフラグファイルの拡張子を".flag"にするといった形で事前に決めておき、
パラメータ属性の[ValidatePattern(".flag$")]のように厳密に設定するのも良いと思います。


# 別の方法
こういう連携はよくあるため、運用系のソフトウェアで同様の機能を提供しているものはあります。
JP1/AJSには「ファイル監視ジョブ」という仕組みがありますし、ファイル連携ソフトのHULFTにもファイルトリガーといった仕組みがあります。

昨今はクライアント自動化という点も注目されだしたので、
サーバに限らずクライアント端末でこのようなフラグ連携による自動化を進めていっても面白いですね。
(個人ユースの端末にAJSやHULFTを入れるのは中々金銭的にも現実的ではなさそうですしね:flushed:)