動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

PowerShell でファイルを一括リネーム・仕分け・古いファイル自動削除する(ログ/帳票整理)

バイブス父さん
現役の業務SE
2026年7月11日13 min read
PowerShell でファイルを一括リネーム・仕分け・古いファイル自動削除する(ログ/帳票整理)

共有フォルダの「ログ」ディレクトリ、開いた瞬間に数千ファイル並んでて固まった、みたいなこと、ないですか??

日次バッチが吐くログ、月末の帳票、エクスポートした CSV。消す担当が決まってないと、ひたすら積み上がる。で、ある日「古いの整理して」と降ってくる。エクスプローラーで日付ソートして、手で選択して……を数千件。地獄です。

どうも、ヒロポンです。

今回は PowerShell でファイルを一括リネーム・仕分け・古いファイル自動削除する話。溜まる一方のログや帳票を、手作業をやめて1本のスクリプトに寄せます。1人でファイル整理を回してる業務SE 向け。

先に一番大事なことだけ言っておきます。削除はいきなり実行しない-WhatIf で空撃ちして「何が消えるか」を目に見える形で確認してから本番。ここを飛ばすと、消しちゃいけない現行ファイルを巻き込みます。

💡 同じ「1人運用の定期ジョブ自動化」シリーズです。PowerShell で SQL Server のバックアップを自動化するAD バルクユーザー作成 と並べて、整理系のジョブとして組み込むと効きます。別タブで開いて後で読んでくださいな。

結論: フィルタ → リネーム/仕分け → 保持期間で削除。削除は -WhatIf から

先に答え。ファイル整理は、この3段で組むと安全に回ります。

  1. フィルタ: Get-ChildItem で対象ファイルを掴む(拡張子・日付で絞る)
  2. リネーム / 仕分け: Rename-Item で名前を揃える、Move-Item でフォルダに振り分ける
  3. 保持期間で削除: LastWriteTime が N 日より古いものを Remove-Item必ず -WhatIf で空撃ちしてから

3 の削除だけは、いきなり本番で流さない。-WhatIf を付けると「実際には消さずに、何が消えるかだけ表示」してくれます。目で見て納得してから -WhatIf を外す。この一手間で、いい感じに事故が消えます。

なぜ手作業のファイル整理をやめるべきか

ファイルが溜まると、地味に効いてくる問題が2つあります。

ひとつは棚卸しがしんどくなる。「このフォルダ、何がどれだけ溜まってる??」に手作業だと答えられない。監査で「古いログの保持ポリシーは?」と聞かれて詰まる。

もうひとつはディスクを静かに食う。ログが GB 単位で積み上がって、ある日ディスク警告が飛んで初めて気づく。

これ、スクリプトで「毎週フィルタして、古いのは消す」を回しておけば全部防げます。手でやる作業じゃないんですよね。

最短対処: コピペで動く3段スクリプト

流れはこんな感じ。フィルタ → 仕分け → 保持削除を、そのまま貼れる形にします。

ファイル整理の安全フロー: Get-ChildItem でフィルタ、Rename-Item で改名、Move-Item で仕分け、LastWriteTime で古いファイルを判定、-WhatIf で空撃ち確認してから Remove-Item で削除する流れ

Step 1: Get-ChildItem でフィルタして対象を掴む

まず整理対象を掴む。拡張子で絞って、必要ならサブフォルダも見る。

$targetDir = "D:\logs"

# .log ファイルだけを再帰的に取得
$files = Get-ChildItem -Path $targetDir -Filter "*.log" -Recurse -File

Write-Host "対象: $($files.Count) 件"

実行結果(.log だけ・サブフォルダ含む・ディレクトリ除外):

Get-ChildItem -Filter *.log -Recurse -File が .log ファイルだけをサブフォルダ含めて取得し、.txt やディレクトリは除外した実行結果

-Filter "*.log" で拡張子を絞り、-Recurse でサブフォルダも辿る。-File を付けるとフォルダを除外してファイルだけになります。この段階では掴むだけ・何も変更しない。まず件数を出して規模感を掴んでおくと、あとが安心です。

Step 2: Rename-Item / Move-Item で仕分け

名前を揃えたいなら Rename-Item。パイプで渡して、-NewName にスクリプトブロックで新しい名前を組み立てます。

# 例: app.log → app_2026-07-05.log のように日付を付ける
Get-ChildItem -Path $targetDir -Filter "*.log" -File | Rename-Item -NewName {
    "$($_.BaseName)_$($_.LastWriteTime.ToString('yyyy-MM-dd'))$($_.Extension)"
} -WhatIf

年月フォルダに振り分けるなら Move-Item。移動先フォルダを先に作っておきます。

Get-ChildItem -Path $targetDir -Filter "*.log" -File | ForEach-Object {
    $ym = $_.LastWriteTime.ToString("yyyy-MM")
    $dest = Join-Path $targetDir $ym
    if (-not (Test-Path $dest)) { New-Item -ItemType Directory -Path $dest | Out-Null }
    Move-Item -Path $_.FullName -Destination $dest -WhatIf
}

リネームも移動も、まず -WhatIf を付けて結果を見てから外す。いい感じに揃うのを確認してから本番です。

Step 3: LastWriteTime で古いファイルを -WhatIf → 削除

保持期間を過ぎたファイルを消す。LastWriteTime で N 日より古いものに絞ります。

$retentionDays = 90
$limit = (Get-Date).AddDays(-$retentionDays)

# まず -WhatIf で「何が消えるか」だけ表示(実際には消さない)
Get-ChildItem -Path $targetDir -Filter "*.log" -Recurse -File |
    Where-Object { $_.LastWriteTime -lt $limit } |
    Remove-Item -WhatIf

実行結果(-WhatIf は「消える対象」を表示するだけで実際には消さない・古いものだけが対象になる):

保持期間より古いファイルだけが Remove-Item の対象になり、-WhatIf を付けると What if 表示が出るだけでファイルは消えずに残る実機結果

出力を眺めて、消える対象が想定通りだと確信できたら、-WhatIf を外して本番。

Get-ChildItem -Path $targetDir -Filter "*.log" -Recurse -File |
    Where-Object { $_.LastWriteTime -lt $limit } |
    Remove-Item -Force

これで「90 日より古い .log は自動で消える」が1本のスクリプトになる。タスクスケジューラで週1回回せば、もう手で整理しなくて済みます!!

ハマりポイント: 知らないと現行ファイルを巻き込む

ハマり1: いきなり Remove-Item で消してはいけないファイルを巻き込む

一番怖いやつ。フィルタが緩いまま Remove-Item を流すと、まだ使ってる現行ファイルまで消える

たとえば -Filter "*.log" のつもりが、いま書き込み中の app.log も条件に入ってた、みたいな。LastWriteTime の閾値がちょっとズレてて、昨日のログまで消えた、とか。

俺も一度、テスト用のつもりで組んだスクリプトを本番フォルダで動かしかけて、-WhatIf の出力に見覚えのあるファイル名が並んでて手が止まったことがあります。あれで -WhatIf を付けてなかったら、と思うと今でも冷や汗が出る。

対策は徹底して-WhatIf で空撃ち → 目視 → 本番の順を守ること。あと、現行ファイルを除外する条件(書き込み中のファイル名を除く、閾値に余裕を持たせる)を1つ入れておく。ここだけは横着しないのが正解です。

ハマり2: -Filter と -Include で対象がずれる

-Filter は速いんですが、単純なワイルドカード1パターンしか使えない。「.log と .txt 両方」みたいな複数条件は書けません。

# これは動かない(-Filter は1パターンだけ)
Get-ChildItem -Filter "*.log,*.txt"      # ← 期待通りに絞れない

# 複数拡張子は -Include(-Recurse かパス末尾ワイルドカードが要る)
Get-ChildItem -Path "D:\logs\*" -Include "*.log","*.txt" -Recurse

-Filter はプロバイダ側で動くから速い。ただし機能は最小限です。複数条件や複雑な絞り込みは -IncludeWhere-Object に切り替える。ん? カンマ区切りで並べたのに絞れてない??ってなったら、だいたい -Filter の仕様です。

ハマり3: Move-Item は既定で上書きしない(-Force が要る)

Move-Item は、移動先に同名ファイルがあると既定でエラーになります。Copy-Item の感覚で「上書きされる」と思ってると、仕分けの途中でスクリプトが止まる。

Move-Item -Path $src -Destination $dest          # 同名があるとエラーで停止
Move-Item -Path $src -Destination $dest -Force    # -Force で上書き

これ、実は安全側の挙動でもあります。-Force を付けなければ既存ファイルの誤上書きを防げる。だから「エラーで止まった=上書き事故を防いでくれた」と捉えて、本当に上書きしていい時だけ -Force を付ける。それがいいです。

現場メモ: 整理を仕組み化すると棚卸し・監査がラクになる

俺が1人でサーバー周りを見てた時、ログと帳票の出力フォルダが野放しで、気づくとディスク警告が飛ぶ、みたいな運用でした。

そのたびに手で古いのを消してたんですが、これを「毎週フィルタ → 90 日より古いのは削除」のスクリプトに寄せた。あとはタスクスケジューラで回すだけ。

そうすると、ディスク警告が来なくなった。それだけじゃなくて、監査で「保持ポリシーは?」と聞かれた時にスクリプトを見せれば一発で説明できるようになった。「90 日保持、週次で自動削除」が仕組みとして残るんで、口頭の言い訳がいらない。

ファイル整理って地味なんですよね。でも放置すると、ある日ドカンと来る。仕組みにしておくと、棚卸しも監査もラクになります。手でやる作業じゃない、と割り切るのが1人運用のコツかなと。

まとめ

PowerShell でのファイル整理、安全に回すポイント。

  • フィルタGet-ChildItem -Filter -Recurse -File で対象を掴む。まず件数確認
  • リネーム / 仕分けRename-Item -NewName {...} / Move-Item-WhatIf で結果を見てから
  • 保持削除LastWriteTime -lt で古いものに絞って Remove-Item必ず -WhatIf 空撃ちから
  • 上書き注意Move-Item は既定で上書きしない。-Force は本当に必要な時だけ

手作業のファイル整理を1本のスクリプトに寄せると、ディスクも棚卸しも監査も一気にラクになる。まず -WhatIf を付けた状態で保存する癖をつけておけば、事故りようがない。1人運用なら、週1のタスク登録から始めるのがいい感じにハマると思います!!

よくある質問

タスクスケジューラで動かす時、パスは相対でいい?

絶対パスにしてください。タスクスケジューラは実行時のカレントディレクトリが想定と違うことが多く、相対パスだと対象フォルダを取り違えて、最悪違う場所を消しかねません。$targetDir は絶対パスで固定するのが安全です。

-WhatIf の代わりに -Confirm でもいい?

用途が違います。-WhatIf は「何が起きるか一覧で見る」空撃ち、-Confirm は「1件ずつ本当にやるか聞く」対話確認です。大量ファイルの一括削除なら、まず -WhatIf で全体像を見て、個別に慎重にやりたい時だけ -Confirm を使う、と使い分けます。

ログが使用中(書き込みロック)だと削除でエラーになる?

なります。書き込み中のファイルは削除やリネームで「使用中」エラーが出ます。対策は、閾値を余裕を持って取って現行ファイルを対象に含めないこと。エラーを握りつぶさず、-ErrorAction の扱いは別記事 PowerShell の ErrorAction で踏む3パターンの落とし穴 を参考にしてください。

サブフォルダごと消したい / 空フォルダを消したい時は?

Remove-Item -Recurse でフォルダごと削除できますが、これは特に慎重に。空フォルダだけ消すなら、Get-ChildItem -Directory で各フォルダの中身が空かを判定してから消します。-Recurse は強力なので、必ず -WhatIf で対象を確認してから使ってください。

次に読むべき記事

以上!

ログ溜めすぎてディスク警告を食らった経験ある人、けっこう多いと思うんで、どんどんシェア待ってるぜ!!

執筆者

バイブス父さん — 業務 SE 7 年(正社員 2 / フリーランス 5)。現職は SEO 直轄部の AI アドバイザー兼 PL、副業で中小 SIer の CTO。SIer 正社員から独立し、複数のフリーランスエージェント経由で現場を渡り歩いた経験ベースで「業務 SE 視点」の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524(日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について