PowerShellでフォルダ監視2nd(PowerShellのおかげで女子から相談された話)

#忙しい人向けの要約

- PowerShellでフォルダ監視する記事を書いたら見知らぬ女子からDMで相談が来た
- PowerShellだけでフォルダ監視する方法が知りたいらしいのでSleep使った実装を提案 (要件的にSleepのほうが良さそうだったから)
- コンソールアプリでEnter3回を入力する必要があるみたいなので、ついでにパイプで標準入力に渡す方法を教えた(Sendkeys以外にも方法があることを広めたい)

#事の発端

2018年9月頃にPowerShellでフォルダを監視する記事を書きました。
[PowerShellでフォルダ監視](https://qiita.com/happy_packet/items/bb952626aef7323f49b2)

先の事例では、「タスクスケジューラー」と「PowerShell」と「bat(cmd)」をうまく組み合わせて使っていました。

しばらくして、TwitterのDMで見知らぬ女性からメッセージが届きました。
内容を要約すると、タスクスケジューラーでは5秒間隔での監視が難しいのでPowerShellだけで完結できないかという相談でした。
5秒間隔って短すぎるなと思いましたが、詳しい事情を伺うと妥当な理由で納得しました。

今回はその内容をぼかしにぼかして抽象化した事例を取り上げます。

#やりたかったこと

事情をヒアリングするとどうやら、以下のことがやりたいみたいでした。
※ある程度、抽象化して(ぼかして)います。

1. 5秒Sleepする
2. 監視対象ディレクトリに.pdfのファイルがあれば、ExecuteProc.exeを起動してEnter3回入力する
3. 2の処理を$DIRSで指定されたディレクトリすべてで行う。  
3. 1に戻る(無限ループ)

これをやる上での課題はこんな感じかなと。

1. 5秒間隔でフォルダを監視する
2. 監視に引っかかったら、特定のexeを起動してEnterを3回入力する
3. PowerShellの実行ポリシーは、通常Restrictedのままで今回のps1だけ実行できるようにする。
4. 実行時にウィンドウがじゃまにならないようにする

1については今回はスリープを使えばどうにかなりそうです。(厳密に5秒間隔の監視だったら並列処理などが必要ですが)
2については既に動くコードがあるらしいので、あまり触れませんでしたが内容から察するにコンソールアプリだと思うので適当にダミーの実行ファイルを作ることにします。
3と4については、過去の記事で触れているため課題はクリアです。

以下、1と2について詳細に解説するためのデモコードを書きました。
Githubにもコードを置いて置きますので良ければ使ってください。
https://github.com/iranika/Win-Check-Dir2

#5秒間隔でフォルダを監視する

とりあえずコードをどうぞ。

```Check-Dir2.ps1
param(
    [int]$interval_time = 5 #デフォルトで5秒
    ,[bool]$debug = $false #デバッグモードのフラグ
)

#エラーが起こった際にスクリプトを止めるかどうか
$ErrorActionPreference = "Continue"

#監視対象のフォルダ
$DIRS=@(
    ".\sample\Dir01"
    ,".\sample\Dir02"
)
$log_file = "./debug.log"

#ループ処理
$is_loop = $true
while ($is_loop) {
    #デバッグ用
    if ($debug -eq $true){ Get-Date -Format "yyyy/MM/dd hh:mm:ss:ff" >> $log_file }
    #interval_time秒だけスリープ
    Start-Sleep -Seconds $interval_time

    #実行したい処理を実行
    foreach($dir in $DIRS){
        $hit_files = (dir $dir/*.pdf | Measure-Object).Count #ヒットした数
        #$hit_files = (dir -Recurse $dir/*.pdf | Measure-Object).Count #サブフォルダも検索する場合
        
        if ($hit_files -gt 0){ #上の処理でヒットした数が0以上
            #ExecuteProc.exeに改行3つを渡す
            echo "`n`n`n" | &"./ExecuteProc.exe"

        }else{
            #pdfがないときの処理を書く
        }
    }

    <#
    #本来であればスケジュール処理と監視処理は別ファイルにしたり関数に分けたりしたほうが良さそう
    #例えばExecute-Proc.ps1に切り出して、呼び出すとか

    &"./Execute-Proc.ps1"   #Execute-Proc.ps1を実行
    
    #こうしておけば、スケジュール部分は何でも良くなるから便利
    #>
}
```

今回は相談の内容を聞いたところ、

1. 5秒待つ
2. フォルダをチェックして条件にマッチしたら、特定の処理を実行
3. 処理が終わり次第5待つに戻る

という処理が良さそうだったので順次実行するようにしています。

個人的には例外時の処理を考慮していませんし、もっといい方法があるような気がします。
まぁ9割くらいはこれで動くでしょうし、コード量が増えると読みにくくなるので今回は仕方ないことにします。
(流石に本番で使うならしっかり考えますが、今回はデモなので許容することにします)


あとEnter3回入力については、
今回はコンソールアプリなので標準入力を使いました。

```ps1
echo "`n`n`n" | &"./ExecuteProc.exe"
```

標準入力はshとかでも使える機能なので、覚えておいて損はないと思います。
あとGUIなどの場合はSendKyesを使えば行けると思います。

#フォルダの定期監視について

そもそもなんですが、フォルダ監視するフリーソフトがあったりするので監視部分にそれを使うのもアリだと思います。
今回の相談者もフリーソフトは既に試しいて動いていたそうです。
ただ、PowerShellに一本化できたらカッコいいなというのも相談の要因だったとか。

スケジューラー系の処理で、正確性と安定稼働を求めるなら本業のソフト(タスクスケジューラー等)に任せてしまうのが無難な気がします。
スクリプトで常時起動となると、何が起こるかわからないので(気づかずにウィンドウ閉じちゃったり、etc...)
そのあたりのリスクに理解があってコードが書けるなら、スクリプトもしくはC#でアプリ化するほうが確かに楽かもしれません。
自分で好きなように設計できますし、柔軟に処理を書き換えられるので。

ちなみにタスクスケジューラーでの5秒間隔で監視する方法として、
日時指定で0時0分5秒,0時0分10秒,…みたいに時刻スケジュールで乗り切る力技もあったりします。
(うまく動くかわかりませんが…)

あと.NET FrameworkにはFileSystemWatcherクラスという便利な機能があるので、
.NETプログラミング出来る人は使ってみるのもありだと思います。
https://docs.microsoft.com/ja-jp/dotnet/api/system.io.filesystemwatcher?view=netframework-4.7.2
※ドキュメントを読んだ感じだと、少しハマりどころがありそうですが


#余談
お酒飲みながら書いたので、独り語りがちょっと多いです。
特に技術的なこと書いてないので下記内容はエンジニアの方は読まなくていいと思います(何)

## 女子から声かけられたのはPowerShellが初めて
PythonやNimを書いていても女子から声かけられることはありませんでしたが、
PowerShellを書いていて声かけられたのは個人的に貴重な経験でした笑。

女子から相談されたいエンジニアはPowerShellを書こう！！(冗談)

## なぜPowerShell？

PowerShellの記事をちょこちょこ書いているのですが、
Shellに慣れ親しんでもらいたいという目的があって記事を書いたりしています。
ターゲットは「プログラミングとかよく知らんけど、自動化(何かしらの作業を効率化)したい！」みたいな人です。
私が育った職場では、コードを書ける人がほとんどいませんでした。
そういう人たちにコードが書けるようになってもらうにはどうすればいいか考えたときに、下記の流れがいいのかなと。
(あくまで個人の感想です)

1. PowerShellで簡単にツールが作れることを体感してもらう
2. PowerShellに慣れ親しんで、シェルのスクリプトが組めるようになってもらう
3. だんだんシェルでは届かない痒いところがわかり、.NETの存在を知ってもらう
4. .NETの沼の住人になる

ちなみに私はC言語からプログラミングに入門したので昔はシェルのことをよくわかっていませんでした。
UNIX哲学本の「UNIXという考え方」を読んでからシェルの役割と利便性がよくわかり、「[コマンドプロンプトからPowerShellに乗り換えるための小さな本](https://qiita.com/tadnakam/items/f51e03021b95eb39f34b)」でPowerShellに入門してから、PowerShellの面白さと利便性を知りました。
そしてPowerShell Core6.0に出会い、Linux系のOSにPowerShellがプリインストールされている未来を夢見ていたり…

なのでプログラミングできる方からすると「ゴミみたいな記事書いてるな～」と思われるかもしれませんが、もしかするとこんな記事のおかげでコード書く女子が増えるかもしれないので許してください。
あと、将来的にはPowerShell→C#への記事とか書こうと思ってるので許してください。

以上。