指定期間に変更されたファイルを探すときは、forfiles /D の符号を逆に覚えないことが最重要です。/D +<date> は指定日以後、/D -<date> は指定日以前です。絶対日付はWindows端末と同じ地域形式で指定します。相対指定 /D -N は「N日前以前」、つまり古い側を選び、直近N日ではありません。まず /C "cmd /c if @isdir==FALSE echo ..." だけでファイル候補を表示し、削除・移動などの処理は検索コマンドから切り離します。開始と終了を持つ期間は PowerShell で半開区間を指定すると境界を明確にできます。 確認ポイント:forfiles /Dは符号と日付境界を固定し、local時刻とUTCの取り違えを避けます。
forfiles /Dの基準日と符号を固定する
/D +2026/07/01:2026年7月1日当日またはそれより後に変更された項目。/D -2026/07/01:2026年7月1日当日またはそれより前に変更された項目。/D -30:実行日の30日前の日付またはそれより前に変更された項目。30日以内の新しい項目ではない。
絶対日付はWindows端末の地域形式に従います。英語(米国)の例は MM/DD/YYYY、この日本語Windowsで forfiles /? が示す形式は yyyy/MM/dd です。まず実行環境のヘルプを確認し、日本語環境の例では2026年7月1日を 2026/07/01 と入力します。日単位の比較なので、相対指定を「正確に720時間前」と解釈してはいけません。
指定日以後・指定日以前をechoで確認する
2026年7月1日以後
forfiles /P "C:\Data\Reports" /S /M * /D +2026/07/01 /C "cmd /c if @isdir==FALSE echo @fdate @ftime @path"
2026年6月30日以前
forfiles /P "C:\Data\Reports" /S /M * /D -2026/06/30 /C "cmd /c if @isdir==FALSE echo @fdate @ftime @path"
/P は検索開始フォルダー、/S はサブフォルダーを含む指定、/M * は名前の検索マスクです。/M はファイルだけに限定する指定ではなく、forfilesはディレクトリエントリも列挙できます。主要3例では /C の中で if @isdir==FALSE を評価し、ディレクトリーを除外してファイルだけを表示します。@path は完全パス、@fdate は最終更新日、@ftime は最終更新時刻へ展開されます。/C の引数全体を二重引用符で囲み、その中で cmd /c if @isdir==FALSE echo を実行します。@path 自体は引用された形式で展開されるため、空白を含む名前も表示できます。
この段階の出力例は 07/03/2026 02:15:08 PM "C:\Data\Reports\Q2 final.xlsx" のようになります。日付・時刻の表示は環境の形式に依存します。該当なしでは「エラー: 指定された検索条件でファイルが見つかりませんでした。」相当のメッセージが出るため、標準出力の0行だけでなくエラー表示も読みます。
古いファイル候補を相対日数で出す
forfiles /P "C:\Logs\Archive" /S /M *.log /D -30 /C "cmd /c if @isdir==FALSE echo @fdate @ftime @path"
これはログのうち「30日前の日付以前」を表示するドライランです。「過去30日間に変更されたログ」を表示するコマンドではありません。たとえば実行日が2026年7月17日なら、概ね2026年6月17日以前の日付が対象側です。時刻までのローリング30日ではないため、保持期限が時間単位の運用には使いません。
結果が正しく見えても、echo を削除、移動、任意スクリプトへ直接置き換えないでください。まず完全パス、件数、最古・最新日時、除外すべきフォルダーをレビューし、後続作業が必要なら承認済みの固定リストへ分離します。検索条件の誤りを、破壊的処理の実行後に検出しても元に戻せない場合があります。
1日だけ、または任意期間を確実に絞る
forfiles の /D 一つでは開始と終了の両方を同時指定できません。2026年7月5日だけを対象にする例では、PowerShell の開始を当日0時、終了を翌日0時として、-ge と -lt を組み合わせます。終了を排他的にすると、23:59:59.999…のような精度依存の上限を作らずに済みます。
powershell -NoProfile -Command "$start=[datetime]'2026-07-05T00:00:00'; $end=[datetime]'2026-07-06T00:00:00'; Get-ChildItem -LiteralPath 'C:\Data\Reports' -File -Recurse | Where-Object { $_.LastWriteTime -ge $start -and $_.LastWriteTime -lt $end } | Select-Object FullName,LastWriteTime,Length"
7月1日以上、7月8日未満という一週間なら $start を 2026-07-01T00:00:00、$end を 2026-07-08T00:00:00 に変えます。これで7月1日から7日までを含み、8日0時以後を除外します。連続する次の期間を8日0時から始めても重複しません。
ローカル時刻とUTCを選ぶ
LastWriteTime はローカル時刻として比較します。同じWindows端末内の業務日付を調べるなら、その端末のタイムゾーンを記録した上で使えます。複数地域のサーバー結果を一つに集約するなら LastWriteTimeUtc と、末尾Zを持つUTC境界を使います。ローカル値とUTC値を混ぜると境界付近のファイルがずれます。
powershell -NoProfile -Command "$start=[datetime]::Parse('2026-07-01T00:00:00Z').ToUniversalTime(); $end=[datetime]::Parse('2026-07-08T00:00:00Z').ToUniversalTime(); Get-ChildItem -LiteralPath 'C:\Data\Reports' -File -Recurse | Where-Object { $_.LastWriteTimeUtc -ge $start -and $_.LastWriteTimeUtc -lt $end } | Select-Object FullName,LastWriteTimeUtc,Length"
境界サンプルで検証する
検証用フォルダーには、開始より1秒前、開始と同時、期間中、終了より1秒前、終了と同時の5ファイルを用意すると比較演算を確認できます。期待値は開始と同時から終了直前までの3件で、終了と同時は含みません。実ファイルの時刻を変更してテストするのではなく、専用の検証ファイルで実施します。
forfiles と PowerShell で件数が違うときは、日単位と時刻単位、forfiles側のif @isdir==FALSEとPowerShell側の-File、/S と -Recurse、/M のマスク、アクセス拒否、ローカル時刻とUTCの差を順に確認します。最終更新時刻は内容変更の有力な手掛かりですが、コピーや復元ツールが保持することもあり、「その期間に人が編集した」という監査証明にはなりません。
失敗条件と変更時の戻し方
- 検索開始パスが存在しない、またはアクセス拒否が出た場合は不完全な一覧として停止する。
- 日付を
YYYY/MM/DDで渡した、符号の意味を確認していない、直近N日と誤読した場合は条件を修正してechoから再実行する。 - 候補が想定件数を超えた場合は再帰範囲とマスクを狭め、後続処理へ渡さない。
- 表示だけなら元ファイルは変わらずロールバック不要。後続で移動などを行う場合は、echoで確定した固定リストと元の完全パスを保持し、その別工程でのみ逆移動を設計する。
実行前チェック
/D +日付は以後、/D -日付は以前と説明できる。/D -Nを古い側の抽出として使っている。/C "cmd /c if @isdir==FALSE echo @fdate @ftime @path"のファイル限定ドライランを保存・確認した。- 任意期間は開始を含み終了を含まない境界で指定した。
- 0件とエラー、ローカル時刻とUTC、ファイルとディレクトリを区別した。

コメント