質問者
フォルダの自動削除バッチの仕様について

質問
-
顧客の依頼で、特定のPCのDドライブに対し古いファイルを自動削除する仕組みを導入してほしいと言われており、バッチをタスクスケジューラで1日ごとに実行する方向で考えているのですが、バッチを作成するにあたって少々詰まっております。
要件としては「更新日時が3ヶ月以上前のファイルを削除、ただし削除対象でないファイルが1つでも存在する階層が存在した場合、同階層の他ファイルは削除しない」といったものです。
具体的には下記のイメージです。例:2020/12/20にバッチを実行した場合
パターン①
AAAフォルダ
├aaa.txt(更新日時:2020/8/20)
├bbb.txt(更新日時:2020/8/20)
└ccc.txt(更新日時:2020/8/20)↓
中のファイル全て最終更新日時が4ヶ月前なので中のファイル全て削除される
パターン②
AAAフォルダ
├aaa.txt(更新日時:2020/11/20)
├bbb.txt(更新日時:2020/8/20)
└ccc.txt(更新日時:2020/8/20)↓
aaa.txtは更新日時が「2020/11/20」で更新日時が1ヶ月前となっており削除要件を満たしていないため、結果、同階層のbbb.txtとccc.txtは古い更新日時でも削除されない。
このような条件を満たすスクリプトを作成することは可能でしょうか。もしあるならご教授頂きたく。
よろしくお願いします。
- 編集済み shikkoku 2021年1月27日 0:52
すべての返信
-
バッチファイル内からPowerShellスクリプトを呼び出すのは有りなのでしょうか?
例えばbatファイルのほかに<PowerShellScript>.ps1を用意します。
<PowerShellScript>.ps1の中身はざっくりと以下の感じです。
==================================ここから
if( (Get-ChildItem "<チェック対象フォルダパス>" | Where-Object {$_.LastWriteTime -ge (Get-Date).AddMonths(-3)}).Count -ge 1){
#パターン②のルート
#<チェック対象フォルダパス>に対し現在の時刻から3か月前より新しい更新日付のファイルが1以上ある場合のルート(更新日付が3か月以内のものが1つ以上ある)
$exit_Code = 1
}
else{
#パターン①のルート
#<チェック対象フォルダパス>に対し現在の時刻から3か月前より新しい更新日付のファイルが1つもない場合のルート(すべてのファイルの更新日付が3か月前より古い)
$exit_Code = 0
}
exit $exit_Code
==================================ここまで
でバッチファイルから<PowerShellScript>.ps1を呼び出しエラーレベル(実行結果)を取得して1なら何もしない。0ならすべて削除する
powershell.exe -ExecutionPolicy RemoteSigned -File ".\<PowerShellScript>.ps1"
set resultcode=%ERRORLEVEL%といった感じはどうでしょうか?条件にバッチファイルのみで完結させる必要がある場合は使用できませんが、Powershellを
使用できるのであれば条件抽出の部分が簡単になりそうな感じはしています。
- 編集済み Savior13 2021年1月29日 5:18
-
© ウィンドウズスクリプトプログラマ - Windows Script Programmer 2020
for /r . /d %%I in (*) do forfiles /p %%I /d +2021/01/31 || del %%I /q
かな。- 編集済み ウィンドウズスクリプトプログラマ 2021年1月30日 12:12 アブナイアブナイ
-
© ウィンドウズスクリプトプログラマ - Windows Script Programmer 2020
2,3は当然。directory entryのプライマリキーが変化するようなものは、更新日時が変わる。
メモ帳で開いて、上書き保存すると、ファイルの更新日時は変わるが、ディレクトリの更新日時は変わらない。
ファイルの更新日時を直接書き換えても、ディレクトリの更新日時は変わらない。
フォルダの更新日時更新されない。 - マイクロソフト コミュニティ (microsoft.com)- 編集済み ウィンドウズスクリプトプログラマ 2021年2月2日 3:08
-
返信が遅くなってしまい申し訳ありません。
皆様ご回答ありがとうございます。
既にご指摘があったように説明が不足してたので補足させて頂きます。
・バッチ処理の対象範囲はDドライブ直下のフォルダのサブフォルダから再帰的に処理する。
つまり下記の例でいくと「hoge1」フォルダは処理対象外で、「AAA」フォルダからサブフォルダ含めて再帰的に処理を行う想定です。
Dドライブ
└hoge1フォルダ →処理対象外
└AAAフォルダ →処理対象
└BBBフォルダ →処理対象
・バッチ処理の際、フォルダ内が空の場合はフォルダごと削除する
これは逆に言うとフォルダ内のファイルが全て削除されたとしても中にサブフォルダが存在したら、フォルダは削除されないということになります。
Dドライブ
└hoge1フォルダ
└AAAフォルダ
└aaa.txt →削除対象
├bbb.txt →削除対象
└BBBフォルダ
上記の場合だと、「aaa.txt」と「bbb.txt」削除されても、AAAフォルダのサブフォルダであるBBBフォルダは存在してるので、AAAフォルダは削除されないようにしたいです。
となると、AAAフォルダの最下層から再帰的に処理する仕組みが必要なのかなと考えております。
使用する言語にこだわりはなくPowerShellでも全然構いません。
申し訳ありませんがご教授のほどよろしくお願いします。
- 編集済み shikkoku 2021年2月3日 8:33
-
Set-location D:\hoge1
foreach ($Target in (Get-Childitem -Recurse)) {
if ((Get-Childitem -path $Target |Where-Object{($_.Lastwritetime -ge (get-date).AddMonths(-3)) -or ($_.mode -like 'd*')}).count -lt 1)
{
Remove-Item $Target.Fullname -recurse -force
}}
こんな感じ?※一部Savior13さん記載の条件をそのまま流用しています。
実際には削除処理を含むのでバックアップを取得したうえでのテストをしてください。削除したフォルダ名をログに残したい場合は、Remove処理の前後で$Target.Name.ToString()を出力してください。- 編集済み チキン 2021年2月3日 23:48
-
© ウィンドウズスクリプトプログラマ - Windows Script Programmer 2020
となると、AAAフォルダの最下層から再帰的に処理する仕組みが必要なのかなと考えております。
それは、こう。
call :sub .
rem pause
goto :eof
:sub
for /d %%I in ("%~1\*") do (
call :sub "%%~I"
forfiles /p "%%~I" /d +2021/01/31 || del "%%~I" /q
rd "%%~I"
)
バッチでネックなのは、
更新日時が3ヶ月以上前
これが簡単に計算できないこと。スクリプトに頼るしかない。なら全部スクリプトでやればよい。チャンチャン。- 編集済み ウィンドウズスクリプトプログラマ 2021年2月3日 14:56