都度調べていたforfilesコマンドをもう一度整理する

更新から一定期間を過ぎたファイルを削除する必要に迫られているとき、Windowsではどのようにすれば良いか、ということを都度調べては見よう見まねでやっていた部分がありヒヤッとしたので、forfilesコマンドについて改めてまとめておきます。

##コマンド実行環境
今回は、以下のようなフォルダ/ファイル構成を例に検証します。

Test\
　┣ 01\
　┃  └ 123.txt
　┃　
　┣ 02\
　┃  └ 456.txt
　┃
　┣ 03\
　┃  └ 789.txt
　┣ abc.txt

##実際に使ってみる

調べるとファイルの一斉削除とセットで出てくることが多いコマンドですが、他にも使い方はたくさんあります。

とはいえ個人的な需要は削除に関することなので、そちらにフォーカスして進めていきます。
念のためヘルプから。

```
C:\work\test>forfiles /?

FORFILES [/P パス名] [/M 検索マスク] [/S]
         [/C コマンド] [/D [+ | -] {yyyy/MM/dd | dd}]

説明:
    ファイル (または、ファイルのセット) を選んで、そのファイル上の
    コマンドを実行します。これはバッチ ジョブの使用に便利です。

パラメーター一覧:
    /P    パス名        検索を開始するパスを示します。
                        既定のフォルダーは現在実行中の
                        ディレクトリ (.) です。

    /M    検索マスク    検索マスクによってファイルを検索します。
                        既定の検索マスクは '*' です。

    /S                  サブディレクトリに対しても処理を行うように forfiles に
                        指示します (例: "DIR /S")。

    /C    コマンド      各ファイルの実行するコマンドを示します。
                        コマンドの文字列は二重引用符で囲んでくだ
                        さい。

                        既定のコマンドは "cmd /c echo @file" です。

                        次の変数をコマンドの文字列に使用することができ
                        ます:
                        @file    - フィルの名前を返します。
                        @fname   - 拡張子なしのファイル名を
                                   返します。
                        @ext     - ファイルの拡張子だけを返し
                                   ます。
                        @path    - ファイルの完全なパスを返します。
                        @relpath - ファイルの相対パスを返し
                                   ます。
                        @isdir   - ファイルの種類がディレクトリの場合
                                   は "TRUE"を、ファイルの場合は
                                   "FALSE" を返します。
                        @fsize   - ファイルのサイズをバイトで返し
                                   ます。
                        @fdate   - ファイルの最終更新日を返し
                                   ます。
                        @ftime   - ファイルの最終更新時刻を返し
                                   ます。

                        コマンド ラインに特殊文字を使用する場合は、
                        文字を 16 進数コードで 0xHH 形式で (例:
                        タブは 0x09) 指定してください。CMD.EXE の
                        内部コマンドの前には "cmd /c" が必要
                        です。

    /D    日付          最終更新日が指定された日かもしくはそれ
                        以降 (+)、または指定された日またはそれ
                        以前 (-) であるファイルを "yyyy/MM/dd" 形式を使っ
                        て選択します。または、最終更新日が現在
                        の日にちより "dd" 日あと、もしくは "dd" 日
                        前であるファイルを選択します。有効な "dd"
                        値は 0 から 32768 の間です。
                        指定がない場合は、"+" が既定で使用さ
                        れます。
```

### 実行フォルダパス指定
```
C:\work\test>forfiles /p C:\work\test
"01"
"02"
"03"
"abc.txt"
```
カレントに存在するフォルダとファイル両方がリストされます。

###サブディレクトリまで探索してリストする
```
C:\work\test>forfiles /p C:\work\test /S

"01"
"02"
"03"
"abc.txt"
"123.txt"
"456.txt"
"789.txt"
```

###テキストファイルのみ指定してリストする
```
C:\work\test>forfiles /p C:\work\test /S /M *.txt

"abc.txt"
"123.txt"
"456.txt"
"789.txt"
```

###最終更新が3日以上前のテキストファイルのみリストする
```
C:\work\test>forfiles /p C:\work\test /S /M *.txt /D -3
エラー: 指定した検索条件でファイルが見つかりませんでした。
```
検証環境を作ってから3日以内にこの記事を書いていますので、リストされません。

では、Powershellにてフォルダやファイルの更新日時を変更してから、再度実行してみたいと思います。

```powershell
PS C:\work\test> Set-ItemProperty C:\work\test\01 -Name LastWriteTime -Value "2019/01/01"
PS C:\work\test> Set-ItemProperty C:\work\test\02\456.txt -Name LastWriteTime -Value "2019/01/01"
```
今回は以下のフォルダー・ファイルを変更しました。
Test\
　┣ 01\　←更新日時を2019/01/01に変更
　┃  └ 123.txt
　┃　
　┣ 02\
　┃  └ 456.txt　←更新日時を2019/01/01に変更
　┃
　┣ 03\
　┃  └ 789.txt
　┣ abc.txt

forfilesコマンドをもう一度試してみます。

```
C:\work\test>forfiles /p C:\work\test /S /M *.txt /D -3

"456.txt"

C:\work\test>forfiles /p C:\work\test /S /D -3

"01"
"456.txt"

```
リストされました。

##本題：一定期間更新されていないファイルを削除したい

結論としてはこのようなコマンドになります。

####3日以上更新されていないテキストファイルを削除

```
C:\work\test>forfiles /P C:\work\test /S /D -3 /M *.txt /C "cmd /c echo del /F /Q @path"

del /F /Q "C:\work\test\02\456.txt"
```
実際に削除する際は「echo」を抜いてください。
**ちなみに、ここでちゃんと検索マスク(/M *.txt)を指定しないとフォルダもリストされてしまうので、その配下にあるファイルも削除されてしまいます。**

試しにやってみます。

```
C:\work\test>forfiles /P C:\work\test /S /D -3 /C "cmd /c echo del /F /Q @path"

del /F /Q "C:\work\test\01"
del /F /Q "C:\work\test\02\456.txt"
```

↑のコマンドを実行すると、

Test\
　┣ 01\　←消される
　┃  └ 123.txt　←これも消されてしまう(**良くない**)
　┃　
　┣ 02\
　┃  └ 456.txt　←消される
　┃
　┣ 03\
　┃  └ 789.txt
　┣ abc.txt


フォルダ配下にファイルなどが配置されれば、フォルダの更新日時も自動的に変わるハズなのですが、
WindowsUpdateや何らかの原因でそれが機能しない・しなかったということもありますので、
リスク管理という面でも検索マスクの設定は非常に重要だと感じます。

フォルダごと消しても良い場合は、検索マスクを設定する必要はありません。

ちなみに、/Dオプションで参照されるのは最終更新日時(LastWriteTime)であり、
ファイルの作成日時(CreationTime)ではありません。