Powershellでフォルダ監視をする

ひとりDevOps活動の成果です。
ご査収ください。

# やりたいこと
フォルダにファイルがあるか監視する(すごいシンプル)
今回はファイルがあったらメディアを再生することにします。

# 背景
Windowsでフォルダ監視したくなったことはありませんか？
私はあります。
例えば、「資料提出用の共有フォルダがあって、そこに資料が提出されていたら通知する」みたいな要求です。
私は、バッチファイル+Powershell+タスクスケジューラを組み合わせて実現しましたが、
他にもスマートな方法があるかもしれません。


# 大まかな構成
各所の役割はこんな感じです。

check-dirs.ps1：指定フォルダにps1ファイルがあったらメディアを再生するスクリプト
lunnch_ps1.bat：タスクスケジューラからcheck-dirs.ps1を実行できるようにする中継スクリプト
タスクスケジューラ：定期的にlunch.batを呼び出す。

とりあず、C:\check_dirsというフォルダを作って
check-dirs.ps1とlunch_ps1.batはそこに保存することにします。
※2018/09/20 GitHubにソース公開しておきました。
https://github.com/iranika/win_dir_checker


## まずはPowershellを書く(check-dirs.ps1)
指定フォルダにps1ファイルがあった場合にメディアを再生するスクリプトを書きます。
私の環境では、wavファイルは既定のアプリでMediaPlayerが立ち上がる設定にしてあります。
$DIRSの値は、任意のフォルダのパスを記載してください(例:\\hogehoeg\hugahuga。


```powershell:check-dirs.ps1
# 複数フォルダを指定できる設計になっています(配列)。
$DIRS=@(
"<指定フォルダ1のパス>",
"<共有フォルダのパスもOKです>"
)

# メインの処理
foreach($DIR in $DIRS){
    $result = (dir $DIR/*.ps1)  # dir $DIR/hoge.xlsx にすれば、hoge.xlsxファイルだけに絞れます
    if ($result.Length -ne 0){  # 上の処理でヒットした数が0以外＝ps1がN個ある
        #ps1があるときの処理を書く
        echo "ps1ファイルが存在します"
        #下記のように再生したいメディアのフルパスを書くと既定のアプリで再生することもできます
        C:\Windows\Media\Alerm01.wav
    }else{
        #ps1がない時の処理を書く
        echo "ps1ファイルが存在しません"
    ]
}
```

指定したフォルダにhoge.ps1ファイルを置いてみて、check-dirs.ps1を実行してみましょう。
ps1ファイルの実行ポリシーで躓いたひとは、手を上げてください(Powershell初学者の大半が挙手する)。
Windows Server2012 R2以外のPowershellでは初期設定でps1ファイルの実行ポリシーがRestricted(全てのスクリプトが実行禁止)です。
参考記事：[@IT WindowsでPowerShellスクリプトの実行セキュリティポリシーを変更する](http://www.atmarkit.co.jp/ait/articles/0805/16/news139.html)

ちなみに各所でPowershellのポリシー設定を「Set-ExecutionPolicy RemoteSigned」で変えてしまう方法をよく目撃しますが、
「常に実行可能である状態にしておく」というのはあまり好ましくないと思います。
なので、バッチファイルで「指定したps1ファイルを、一時的にExecutionPolicyのPowershellで実行する」のがよいかなと。
lunch_ps1.batの話に続きます。

## ExecutionPolicyのPowershellでps1ファイルを実行させる(lunch_ps1.bat)
早速ですがlunch_ps1.batのコードです。

```:lunch_ps1.bat
@echo off
echo "ps1ファイルを実行します"
powershell -NoProfile -ExecutionPolicy Unrestricted c:\\check_dirs\check-dirs.ps1
pause > nul
exit
```

これは、ExecutionPolicyのPowershellにcheck-dirs.ps1を実行させています。

こうすることで、大抵のWindows環境ならSet-ExecutionPolicyで環境設定を変更することなく実行できます。
加えて、バッチファイルにすることでダブルクリックでスクリプトを実行できます(ps1はダブルクリックでの実行を容認していません)
環境設定不要でスクリプトがダブルクリックで実行できちゃうなんて素晴らしい！！

これで、フォルダを監視するスクリプトができました。
あとは、これを定期実行するだけです。
(実行してMediaPlayerの初期設定が表示された人は、「既定の設定」で設定して、再度check-dirs.ps1を実行してみてください)


## タスクスケジューラへの登録
タスクスケジューラの操作についてはこちらが参考になります。
参考：https://qiita.com/Richard_Roe/items/44e16841f16ee40c6113
　　　http://www.atmarkit.co.jp/ait/articles/1007/30/news111.html
正直に言うと設定を教えるのが面倒なので、割愛させてください。質問は答えられる範囲で受け付けます。

ちなみに、タスクスケジューラにps1を実行させようとすると面倒です。
バッチは簡単なので、今回はこの構成をあえて採用しています。
ps1を実行させたい人はこちらが参考になります。
参考：http://www.atmarkit.co.jp/ait/articles/1412/03/news125.html

<<2019/04/06追記
この記事の応用系の記事を書きました。よろしければそちらもどうぞ。
[PowerShellでフォルダ監視2nd(PowerShellのおかげで女子から相談された話)](
https://qiita.com/happy_packet/items/53470e6cca4c35b470de)


# 余談
ちなみに、バッチが表示されて邪魔だなと思う人は、

1. バッチファイルのショートカットを作る。
2. ショートカットの[プロパティ]で[ショートカット]タブで実行時の大きさを最小化に設定する。
3. タスクスケジューラでショートカットを実行するように設定

こうすることで、Window最小化された状態で起動されるので問題解決です。
VBAを使ってバックグラウンド実行させようとする記事をよく見かけますが、こちらのほうが簡単でいいと思います。
意外と知られていませんが、ショートカットのプロパティは便利で起動時にウィンドウが表示される位置も指定できたります。

Windowsは意外と知らない機能が結構あるので、気になる人は「Windows 小技」で調べるか、MicrosoftJapanの公式ツイッターがたまに小技を呟いてたりするのでフォローするといいと思います。
個人的に好きな小技は、エクスプローラでロケーションバーに「powershell」と入力するとエクスプローラで開いていたフォルダのロケーションにいる状態でPowershellが開けるやつですね。コマンドにロケーションを引数として渡しているだけなので、何かしらのツールで利用できるかもしれません。
下記は日本マイクロソフトのツイートです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">【メモ帳のちょっとしたコマンド】<br><br>「メモを書いたはいいけど、これはいつの記録だったろうか」<br><br>メモの 1 行目に「.LOG 」と入力すると、ファイルを開くたびに自動で日付を入力してくれるので、こういったことが無くなりますよ。<a href="https://twitter.com/hashtag/Windows?src=hash&amp;ref_src=twsrc%5Etfw">#Windows</a> <a href="https://twitter.com/hashtag/Windows10?src=hash&amp;ref_src=twsrc%5Etfw">#Windows10</a> <a href="https://twitter.com/hashtag/%E5%B0%8F%E6%8A%80?src=hash&amp;ref_src=twsrc%5Etfw">#小技</a> <a href="https://t.co/8Hoe3b3Vjv">pic.twitter.com/8Hoe3b3Vjv</a></p>&mdash; Windows_Japan (@Windows_Japan) <a href="https://twitter.com/Windows_Japan/status/1035482334638874625?ref_src=twsrc%5Etfw">2018年8月31日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>