「サーバーのログフォルダや一時ファイル置き場が容量を圧迫しているけれど、手動で削除するのは手間がかかる」「古いファイルだけを安全に自動削除するスクリプトを作りたいけれど、書き方がわからない」といった悩みを抱えていないでしょうか。
日々の運用管理において、不要になったログファイルやキャッシュの蓄積によるストレージ圧迫は避けられない課題です。PowerShellを活用すれば、指定した日数より古いファイルのみを的確に抽出し、自動でクリーンアップする堅牢なスクリプトを効率的に作成できます。
この記事を読むと、Get-ChildItemやWhere-Object、Remove-Itemといった基本コマンドレットの組み合わせ方から、テスト実行による安全性の担保、実用的なエラーハンドリング、そしてWindowsタスクスケジューラと連携した定期実行の手順までが理解できるようになります。
確認した環境 本記事のスクリプトと手順は、以下の環境で動作を確認しています。
- OSOS [オーエス]Operating System。基本ソフトウェア: Windows 11
- スクリプト言語: PowerShell 5.1
PowerShellで古いファイルを自動削除するメリット
システムの運用を続けるうちに、アプリケーションのログファイル、バックアップの残骸、一時ファイルなどが蓄積され、ストレージの空き容量が徐々に減少していきます。手動で定期的にファイルを確認して削除する作業は、管理者の負担になるだけでなく、削除漏れや誤って必要なファイルを削除してしまうヒューマンエラーのリスクを伴います。
PowerShellを用いた自動削除には、次のようなメリットがあります。
- 作業の完全自動化: タスクスケジューラと組み合わせることで、深夜帯などのシステム負荷が低い時間帯に自動でクリーンアップを実行できます。
- 条件の明確化と維持: 「何日前より古いか」という基準をコードとして明文化するため、属人性を排除した安定した運用が可能になります。
- 安全性の確保: テスト実行機能(
-WhatIf)を活用することで、実際にファイルを削除する前に削除対象を事前に確認できます。
古いファイル削除に使う主要なコマンドレット
PowerShellでファイルやフォルダを操作して削除を行う場合、主に次の3つのコマンドレットをパイプラインで連携させます。
Get-ChildItem: 指定したフォルダ内のファイルやサブフォルダの一覧を取得する。Where-Object: 取得したファイル一覧から、特定の条件(更新日時など)に合致するものを絞り込む。Remove-Item: 絞り込まれたファイルを実際に削除する。
これらのコマンドレットを適切に組み合わせることで、柔軟かつ安全なファイル管理システムを構築できます。
指定日数より古いファイルを検索・特定する方法
ファイル削除スクリプトを作成する第一歩として、まずは「指定した日数より古いファイル」を正確に検索・特定する方法を確認します。
Get-ChildItemとLastWriteTimeの組み合わせ
フォルダ内のファイル情報を取得するには Get-ChildItem(エイリアス: ls や dir)を使用します。各ファイルオブジェクトには、最終更新日時を表す LastWriteTime プロパティが含まれています。
例えば、C:\Logs フォルダ内のファイルを取得するには次のように記述します。
Get-ChildItem -Path "C:\Logs" -File-File スイッチを指定することで、フォルダを除外してファイルのみを対象にできます。
Where-Objectで削除対象の条件を絞り込む
取得したファイル群から、例えば「30日より古いファイル」を抽出するには Where-Object を使用します。現在時刻から一定期間を引いた日時と比較を行います。
現在時刻を取得するには Get-Date を使用し、そこから日数を引くには AddDays() メソッドを利用します。
$days = 30$thresholdDate = (Get-Date).AddDays(-$days)
Get-ChildItem -Path "C:\Logs" -File | Where-Object { $_.LastWriteTime -lt $thresholdDate }ここで使用している -lt は「より小さい(Less Than)」を意味する比較演算子です。実行時点から30日前の日時を基準とし、最終更新日時がその基準日時よりも過去のファイルのみが抽出されます。日付だけでなく時刻も含めて比較するため、厳密には「実行時点から起算して30日前の同時刻より古いファイル」が対象になります。
古いファイルを安全に削除(Remove-Item)するスクリプト
検索条件が正しく機能することを確認したら、次は Remove-Item コマンドレットをパイプラインに繋げて実際の削除処理を行います。
削除処理のサンプルコード
以下は、指定したフォルダ内にある30日以上前のファイルを削除する基本的なスクリプトです。
$targetPath = "C:\Logs"$daysToKeep = 30$thresholdDate = (Get-Date).AddDays(-$daysToKeep)
# 30日より古いファイルを検索して削除Get-ChildItem -Path $targetPath -File | Where-Object { $_.LastWriteTime -lt $thresholdDate } | Remove-Item -Force-Force スイッチを指定すると、読み取り専用属性が設定されたファイルなども削除対象にできます。ただし、アクセス権限が不足している場合や、ファイルが別のプロセスによって使用されている(ロック中)場合など、ほかの要因によって削除に失敗することがあります。-Force を指定すれば、すべてのファイルを必ず削除できるわけではない点に注意してください。
また、Remove-Item -Force は隠しファイルも強制削除できますが、前段の Get-ChildItem -File に -Force が付いていない場合、既定では隠しファイル(Hidden属性)やシステムファイルは抽出対象に含まれません。一時ファイル置き場などで隠し属性のファイルも併せて削除したい場合は、Get-ChildItem にも -Force スイッチを追加してください。
[!TIP] サブフォルダ内のファイルもまとめて削除したい場合 上記のスクリプトは指定フォルダの直下にあるファイルのみを対象とします。サブフォルダ内にある古いファイルも再帰的に検索して削除したい場合は、
Get-ChildItemに-Recurseパラメーターを追加してください(例:Get-ChildItem -Path $targetPath -File -Recurse)。なお、この場合でも空になったサブフォルダ自体は残るため、ファイルのみがクリーンアップされます。 ※ 再帰検索を行うと広範囲のファイルが対象になるため、事前に-WhatIfや一覧確認で対象範囲を慎重に確認してください。
-WhatIfパラメーターでテスト実行する重要性
ファイル削除スクリプトを作成する際、最も注意すべきなのは「誤って必要なファイルを削除してしまうこと」です。エクスプローラーでの手動削除とは異なり、PowerShellの Remove-Item による削除は「ごみ箱」に入らず、即座に完全削除(標準機能では復元不可) されます。
そのため、PowerShellの多くの破壊的コマンドレット(変更や削除を伴うコマンド)には、安全確認用の -WhatIf という共通パラメーターが用意されています。
-WhatIf を指定して実行すると、実際にファイルは削除されず、「もしこのコマンドを実行した場合に何が起きるか」のシミュレーション結果が画面に表示されます。
$targetPath = "C:\Logs"$daysToKeep = 30$thresholdDate = (Get-Date).AddDays(-$daysToKeep)
# 削除対象となるファイルを抽出$oldFiles = Get-ChildItem -Path $targetPath -File | Where-Object { $_.LastWriteTime -lt $thresholdDate }
# 1. 削除対象のファイル一覧を確認$oldFiles | Select-Object FullName, LastWriteTime
# 2. 実際には削除せず、シミュレーションを実行$oldFiles | Remove-Item -WhatIf実行例:
WhatIf: 対象 "C:\Logs\old_20250101.log" に対して操作 "ファイルの削除" を実行しています。※ 画面に表示される文言は、PowerShellのバージョンや表示言語(日本語・英語)によって異なる場合があります。
-WhatIf は「そのコマンドが実行された場合に何が起こるか」を表示する機能であり、削除条件そのものが正しいかどうかを自動で検知するわけではありません。万一、指定パスを誤っていたり日数の計算を間違えていたりすると、誤った条件のまま処理が試行されます。必ず事前に抽出されたファイルの一覧(FullName や LastWriteTime)を確認し、意図したファイルだけが対象になっていることを確かめてから、本番の削除処理を実行してください。
【実践】ログフォルダを自動クリーンアップする応用スクリプト
実務で運用するスクリプトには、処理の成否を記録するログ出力や、対象フォルダが存在しない場合などのエラーハンドリングを組み込んでおくことが重要です。
エラーハンドリングとログ出力を組み込んだ実用的な構成
以下は、処理結果をコンソール出力しつつ、実行ログをテキストファイルとして保存する実用的なスクリプトのサンプルです。
# 設定変数の定義$TargetDirectory = "C:\AppLogs"[int]$RetentionDays = 14$LogFilePath = "C:\Scripts\Cleanup_Log.txt"
# ログ出力用関数の定義function Write-Log { param ([string]$Message) $TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $LogMessage = "[$TimeStamp] $Message"
# ログ出力先フォルダが存在しない場合は作成 $LogDir = Split-Path -Parent $LogFilePath if (-not (Test-Path -LiteralPath $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }
Add-Content -LiteralPath $LogFilePath -Value $LogMessage -Encoding UTF8 Write-Host $LogMessage}
# メイン処理try { # 保持日数のバリデーション(1以上の整数であることを確認) if ($RetentionDays -le 0) { throw "保持日数(`$RetentionDays)には1以上の整数を指定してください。" }
Write-Log "クリーンアップ処理を開始します。対象: $TargetDirectory"
# 対象ディレクトリの存在確認 if (-not (Test-Path -LiteralPath $TargetDirectory -PathType Container)) { throw "対象ディレクトリが存在しないか、フォルダではありません: $TargetDirectory" }
$ThresholdDate = (Get-Date).AddDays(-$RetentionDays) # ファイル取得時のエラー(権限不足等)もcatchできるよう -ErrorAction Stop を指定 # 角括弧などの記号を含むパスでも安全に処理できるよう -LiteralPath を使用 $OldFiles = Get-ChildItem -LiteralPath $TargetDirectory -File -ErrorAction Stop | Where-Object { $_.LastWriteTime -lt $ThresholdDate }
if ($OldFiles) { foreach ($File in $OldFiles) { try { Remove-Item -LiteralPath $File.FullName -Force -ErrorAction Stop Write-Log "削除しました: $($File.FullName) (最終更新: $($File.LastWriteTime))" } catch { Write-Log "削除に失敗しました: $($File.FullName) - $($_.Exception.Message)" } } } else { Write-Log "削除対象となる古いファイルはありませんでした。" }
Write-Log "クリーンアップ処理が完了しました。"}catch { Write-Host "致命的なエラーが発生しました: $_" -ForegroundColor Red try { Write-Log "致命的なエラーが発生しました: $_" } catch { Write-Host "エラーログの書き込みにも失敗しました: $_" -ForegroundColor Red } # タスクスケジューラ等に異常終了を通知するため非ゼロの終了コードを返す exit 1}このスクリプトでは、実運用に耐えうるよう以下の工夫を取り入れています。
- ファイル単位のエラー制御:
foreachループの内側にtry-catchを配置し、Remove-Itemに-ErrorAction Stopを指定しています。これにより、個々のファイルの削除で捕捉可能なエラー(使用中のロックや権限不足など)が発生した場合でも、エラーを記録して次のファイルの処理を続けられます。ただし、ログ出力の失敗やファイル取得時のエラーなどによって、処理全体が正常に完了しない場合もあるため、実行ログやエラー内容を確認することが重要です。 - ワイルドカード誤解釈を防ぐ
-LiteralPath:Get-ChildItemやRemove-Item、Add-Contentのパス指定に-LiteralPathを採用しています。通常の-Pathは[や]などの文字をワイルドカードとして解釈するため、フォルダ名やファイル名に記号が含まれるとエラーや誤動作の原因になります。-LiteralPathを使うことで、指定した文字列がそのまま厳密なパスとして扱われます。 - 異常終了コードの返却(
exit 1): 致命的なエラーをcatchで捕捉した際、画面とログにエラーを出力したうえでexit 1を実行します。これがないとエラー発生時もプロセス全体が「正常終了(終了コード 0)」として終了してしまい、タスクスケジューラ側でタスク失敗を検知できなくなります。なお、一部のファイル削除失敗もタスク全体の失敗として扱いたい場合は、失敗件数をカウントして処理終了時にexit 1を返す設計も有効です。 - 取得エラーの捕捉(終端エラー化):
Get-ChildItemにも-ErrorAction Stopを指定しています。通常、アクセス権限不足などによるファイル取得エラーは処理を継続する「非終端エラー」として扱われ、既定ではcatchされません。-ErrorAction Stopを付与することで、取得失敗時に外側のcatchへ遷移させ、不完全な状態のままスクリプトが正常終了と誤認されるのを防ぎます。 - 入力値とディレクトリの事前確認: 保持日数に対する
[int]型制約と1以上の整数であるかのバリデーションに加え、Test-Path -PathType Containerで対象が実在するフォルダであるかをチェックします。なお、パスの存在確認は対象フォルダへのアクセス権限や削除可能性までを保証するものではないため、実行ユーザーの権限設定も確認してください。 - ログフォルダの自動生成と文字コード指定: ログ出力先(
C:\Scriptsなど)が存在しない場合に記録処理自体が失敗するのを防ぐため、親フォルダの存在チェックと自動作成を組み込んでいます。また、Windows PowerShell 5.1での文字化けや保存揺れを避けるため、Add-Contentに-Encoding UTF8(BOM付きUTF-8)を明示しています。
[!NOTE] スクリプト保存時の文字コードについて Windows PowerShell 5.1で日本語のコメントやメッセージを含むスクリプト(
.ps1)を保存する場合は、「UTF-8(BOM付き)」 で保存する方法が推奨されます。 BOMなしUTF-8で保存すると、Windows PowerShell 5.1がスクリプトをシステムのANSIコードページとして解釈し、日本語の文字化けや構文解析エラーなどが発生する可能性があります。なお、Windows PowerShell 5.1とPowerShell 7以降では文字コードの既定の扱いが異なるため、実行環境に合わせて適切な保存形式を選択してください。
Windowsタスクスケジューラと連携して定期実行する手順
作成したPowerShellスクリプトを定期的に自動実行するには、Windowsの「タスクスケジューラ」を利用します。
ステップ1: スクリプト保存先とログ用フォルダの事前準備
スクリプトやログファイルを保存するためのフォルダ(例: C:\Scripts)を事前に作成しておきます。また、タスクを実行するアカウントがそのフォルダに対して「読み取り・書き込み」権限を持っていることを確認してください。
作成したスクリプトは、このフォルダに保存します(例: C:\Scripts\Cleanup-OldFiles.ps1)。
ステップ2: タスクスケジューラの起動
「ファイル名を指定して実行」(Win + R)を開き、taskschd.msc と入力してタスクスケジューラを起動します。
ステップ3: 基本タスクの作成
右側の操作ペインから「基本タスクの作成…」をクリックし、ウィザードに従って設定を進めます。
- 名前: 任意のわかりやすい名前(例: 「ログ自動クリーンアップ」)
- トリガー: 実行頻度を選択(例: 「毎日」または「毎週」)
- 操作: 「プログラムの起動」を選択
ステップ4: プログラムと引数の詳細設定
「プログラム/スクリプト」および「引数の追加」には、PowerShellスクリプトを安全にバックグラウンド実行するための設定を入力します。
- プログラム/スクリプト:
powershell.exe - 引数の追加:
-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File "C:\Scripts\Cleanup-OldFiles.ps1"
それぞれの引数には次のような重要な役割があります。
-ExecutionPolicy Bypass: 起動するPowerShellプロセスの実行ポリシーを一時的にBypassに設定します。実行ポリシーによる制限でスクリプトがブロックされる場合に利用できますが、グループポリシーで設定された実行ポリシー(MachinePolicy / UserPolicy)など、すべての制限を回避できるわけではありません。組織の管理下にあるPCでは、管理者の運用ルールに従って設定してください。-NoProfile: ユーザープロファイルを読み込まずに起動するため、動作を高速化し、プロファイル起因の競合を防ぎます。-WindowStyle Hidden: PowerShellのコンソールウィンドウを非表示にして起動します。ただし、処理の成功やエラーの発生を保証・抑止する機能ではありません。実行結果を把握できるよう、スクリプトのログファイルやタスクスケジューラの履歴を定期的に確認してください。
ステップ5: 深夜や不在時(サインアウト中)の実行設定とアクセス権
「基本タスクの作成」ウィザードで作成した直後は、初期状態で「ユーザーがログオンしているときのみ実行する」になっています。作業者がPCからサインアウトしている深夜帯でも自動実行させるには、作成後にタスクのプロパティから設定を変更します。
- タスクスケジューラの一覧から作成したタスクを右クリックし、「プロパティ」を開きます。
- 「全般」タブにある「ユーザーがログオンしているかどうかにかかわらず実行する」を選択します。サインアウト中にもタスクを実行するには、タスクの実行アカウントとログオン方式を適切に設定する必要があり、設定保存時にWindowsアカウントのパスワード入力が求められます。タスクの実行アカウントが対象フォルダやログ出力先にアクセスできることも確認してください。
- 対象フォルダの操作に管理者権限が必要な場合に限り、「最上位の特権で実行する」を有効にします(不要な特権は付与せず、タスクの実行アカウントに必要最小限のアクセス権限を設定してください)。
設定が完了したら、タスクを右クリックして手動で「実行」を選択し、指定したログファイル(Cleanup_Log.txt)に実行ログが正しく追記されることを確認してください。
まとめ:PowerShellでストレージ管理を自動化しよう
今回は、PowerShellを使って指定した日数より古いファイルを自動削除するスクリプトの作り方を解説しました。
記事のポイントを以下にまとめます。
Get-ChildItemとLastWriteTime、Where-Objectを組み合わせることで、指定した期間より古いファイルを的確に抽出できる。- 実際に削除を行う
Remove-Itemの前に抽出ファイルの一覧確認や-WhatIfパラメーターを活用することで、誤削除のリスクを抑えて事前検証ができる。 try-catch構文や-ErrorAction Stop、フォルダ存在確認や関数を用いたログ出力を組み合わせることで、実運用に耐える堅牢なスクリプトが完成する。- Windowsタスクスケジューラと適切な実行権限・引数を設定することで、深夜帯などの任意のタイミングでクリーンアップを安定して自動化できる。
ストレージの枯渇リスクや手動運用の手間を削減するため、ぜひ今回のスクリプトをベースにご自身の環境に合わせたファイル管理の自動化を導入してみてください。
以上で本記事の解説を終わります。
よいITライフを!
人気記事
- 1
- 2
- 3
- 4
- 5
思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。