【PowerShell】ファイルを拡張子ごとにフォルダ分けする方法|自動仕分けスクリプト

【PowerShell】ファイルを拡張子ごとにフォルダ分けする方法|自動仕分けスクリプト PowerShell

ダウンロードフォルダやデスクトップが、画像・PDF・テキスト・インストーラーでごちゃ混ぜになっていませんか。PowerShellを使えば、ファイルを拡張子ごとにフォルダへ自動で振り分ける(仕分ける)スクリプトが簡単に作れます。jpgフォルダ、pdfフォルダ、txtフォルダ……と、拡張子ごとのフォルダを作って移動するだけで、散らかったフォルダがすっきり片付きます。

ポイントは、各ファイルの拡張子を.Extensionで取得し、その名前のフォルダを作ってMove-Itemで移動することです。注意点として、.Extension.jpgのようにドット込みで返ること、移動先に同名ファイルがあると衝突してエラーになることがあります。この記事では、実機のPowerShell 5.1で確認しながら、拡張子ごとの自動仕分けを整理します。

先に結論

  • 各ファイルの拡張子は$_.Extensionで取得します(.jpgのようにドット込み)。
  • ドットを除くには$_.Extension.TrimStart(".")とします。
  • 拡張子名のフォルダをNew-Itemで作り、Move-Itemで移動します。
  • 拡張子なしのファイルExtensionが空になるので、専用フォルダに振り分けます。
  • 大文字小文字をそろえるには.ToLower().JPG.jpgを統合)。
  • 同名ファイルの衝突は、連番を付けてリネームすれば回避できます。

対象の取得や移動はファイル・フォルダ操作(Get-ChildItem・Move-Item)、更新日での整理は古いファイルを削除・移動する、重複の整理は重複ファイルの検出もあわせて参考になります。条件での絞り込みはWhere-Object・Select-Objectが便利です。

スポンサーリンク

考え方:拡張子でフォルダを作って振り分ける

自動仕分けの流れはシンプルです。「①フォルダ内のファイルを1つずつ見る → ②そのファイルの拡張子を調べる → ③拡張子名のフォルダ(無ければ作る)へ移動する」という3ステップです。これをForEach-Objectで全ファイルに対して繰り返します。

拡張子を取得する Extension(ドット込み)

ファイルの拡張子は.Extensionプロパティで取得できます。ここで注意したいのが、返ってくる値にドットが含まれることです。

拡張子の取得
$file = Get-Item "C:\data\photo.jpg"

# Extension はドット込みで返る
$file.Extension            # .jpg

# ドットを除いてフォルダ名にしたいとき
$file.Extension.TrimStart(".")   # jpg

# 拡張子なしのファイルは Extension が空文字 ""
(Get-Item "C:\data\README").Extension   # (空)

実機でも、photo.jpg.Extension.jpg(ドット込み)で、.TrimStart(".")を付けるとjpgになりました。フォルダ名にはjpgのようにドットなしを使いたいので、TrimStart(".")でドットを取り除きます。また、拡張子のないファイル(READMEなど)は.Extension空文字になることも確認しました。これは後で専用フォルダに振り分けます。

拡張子ごとに自動仕分けする(基本スクリプト)

それでは基本のスクリプトです。フォルダ内のファイルを1つずつ見て、拡張子名のフォルダを作り、そこへ移動します。サブフォルダは対象にしない-Fileでファイルのみ、-Recurseは付けない)のがポイントです。

拡張子ごとに仕分ける
$dir = "C:\Users\you\Downloads"

Get-ChildItem $dir -File | ForEach-Object {
    # 拡張子(ドットなし・小文字)。無ければ _noext にする
    $ext = $_.Extension.TrimStart(".").ToLower()
    if (-not $ext) { $ext = "_noext" }

    # 拡張子名のフォルダを用意(無ければ作る)
    $target = Join-Path $dir $ext
    if (-not (Test-Path $target)) {
        New-Item -ItemType Directory -Path $target | Out-Null
    }

    # 移動する
    Move-Item $_.FullName -Destination $target
}

実機でも、photo.jpgscan.JPGdoc.txtnote.txt・拡張子なしのREADMEを用意して実行すると、jpgフォルダに2件、txtフォルダに2件、_noextフォルダに1件と、正しく振り分けられました。-Fileを付けてファイルだけを対象にし、-Recurseを付けないことで、作った振り分け先フォルダを再び処理してしまう無限ループを防いでいます

拡張子なし・大文字小文字への対応

上のスクリプトには、実用上の工夫を2つ入れています。拡張子なしファイルの扱いと、大文字小文字の統一です。

拡張子なし・大小の統一
# 拡張子が空(拡張子なし)なら、専用フォルダ名にする
$ext = $_.Extension.TrimStart(".").ToLower()
if (-not $ext) { $ext = "_noext" }

# .ToLower() で .JPG と .jpg を同じ jpg フォルダにまとめる
#   付けないと "JPG" フォルダと "jpg" フォルダに分かれてしまう
.ToLower()で大文字小文字を統一する

実機で確認したところ、.ToLower()を付けることで、photo.jpgscan.JPG同じjpgフォルダにまとめられました。これを付けないと、拡張子が大文字のファイルはJPGフォルダ、小文字はjpgフォルダ、と別々になってしまいます。Windowsではフォルダ名の大文字小文字は区別されませんが、表記を統一しておくと見た目もきれいです。また、拡張子のないファイルはExtensionが空になるため、if (-not $ext) { $ext = "_noext" }_noextのような専用フォルダにまとめると、取りこぼしがありません。

同名ファイルの衝突を避ける

注意したいのが同名ファイルの衝突です。別々の場所にある同じ名前のファイルを同じフォルダへ移動しようとすると、Move-Itemはエラーになります。これを避けるには、移動先に同名ファイルがあるか確認し、あれば連番を付けてリネームします。

同名衝突を連番で回避
Get-ChildItem $dir -File | ForEach-Object {
    $ext = $_.Extension.TrimStart(".").ToLower()
    if (-not $ext) { $ext = "_noext" }
    $target = Join-Path $dir $ext
    if (-not (Test-Path $target)) { New-Item -ItemType Directory -Path $target | Out-Null }

    # 移動先のパスを決める。同名があれば note_1.txt, note_2.txt … にする
    $dest = Join-Path $target $_.Name
    if (Test-Path $dest) {
        $i = 1
        do {
            $newName = "$($_.BaseName)_$i$($_.Extension)"
            $dest = Join-Path $target $newName
            $i++
        } while (Test-Path $dest)
    }

    Move-Item $_.FullName -Destination $dest
}
同名ファイルはそのままだとエラーになる

実機で確認したところ、移動先にすでにnote.txtがある状態で別のnote.txtを移動しようとすると、そのままでは衝突してエラーになりました。上のスクリプトのように、Test-Pathで移動先に同名があるか確認し、あればBaseName(拡張子を除いた名前)に_1_2…と連番を付けてリネームすれば、衝突を回避できます。実機でも、既存のnote.txtを残したまま、新しいファイルがnote_1.txtとして移動されることを確認しました。大事なファイルを上書きで失わないために、この衝突回避を入れておくと安全です。

まず確認する(移動前のプレビュー)

いきなり全ファイルを動かす前に、「どの拡張子が何件あるか」を先に確認すると安心です。Group-Objectで拡張子ごとの件数を表示できます。Move-Item-WhatIfを付ければ、実際に移動せず動きだけを確認することもできます。

移動前に件数を確認
$dir = "C:\Users\you\Downloads"

# 拡張子ごとのファイル数を一覧(仕分け前の確認に)
Get-ChildItem $dir -File |
    Group-Object Extension |
    Sort-Object Count -Descending |
    Select-Object Count, Name

# 実際の移動を試さず動きだけ見るなら、Move-Item に -WhatIf を付ける
# Move-Item $_.FullName -Destination $dest -WhatIf

Group-Object Extensionで拡張子ごとの件数が分かるので、「画像が大量にある」「.tmpが混じっている」といった状況を移動前に把握できます。仕分けスクリプトのMove-Item-WhatIfを付ければ、実際には移動せずに「何がどこへ動くか」だけを確認できます。初めて実行するフォルダでは、まず確認してから本実行すると安心です。

特定の拡張子だけ仕分ける

すべてではなく、画像だけ・書類だけを移動したいときは、Where-Objectで対象の拡張子に絞り込みます。

特定の拡張子だけ対象にする
# 画像ファイルだけを images フォルダへまとめる
$dir = "C:\Users\you\Downloads"
$images = ".jpg", ".jpeg", ".png", ".gif"

$target = Join-Path $dir "images"
if (-not (Test-Path $target)) { New-Item -ItemType Directory -Path $target | Out-Null }

Get-ChildItem $dir -File |
    Where-Object { $images -contains $_.Extension.ToLower() } |
    Move-Item -Destination $target

$images -contains $_.Extension.ToLower()で、拡張子が画像のリストに含まれるファイルだけに絞り込んでいます。.ToLower().JPG.jpgとして判定されます。同じ要領で、.pdf.docx.xlsxdocumentsフォルダへ、といった分類もできます。

主な要素まとめ

拡張子ごとの仕分けで使う要素をまとめます。

要素 働き
$_.Extension 拡張子(.jpgのようにドット込み)
.TrimStart(".") 先頭のドットを取り除く
.ToLower() 大文字小文字を統一する
$_.BaseName 拡張子を除いたファイル名
New-Item -ItemType Directory フォルダを作る
Move-Item -Destination ファイルを移動する
Group-Object Extension 拡張子ごとに件数を集計

よくある失敗

Extensionのドットを忘れる

.Extension.jpgとドット込みです。フォルダ名にはTrimStart(".")でドットを除きます。

-Recurseを付けて無限に処理する

振り分け先フォルダまで再帰処理すると終わりません。-Fileのみで-Recurseは付けません。

同名ファイルでエラーになる

移動先に同名があると衝突します。連番を付けてリネームすると回避できます。

拡張子なしファイルを取りこぼす

Extensionが空のファイルは_noextなどの専用フォルダにまとめます。

大文字小文字でフォルダが分かれる

.ToLower()で統一しないとJPGjpgに分かれます。

よくある質問

Q拡張子ごとにファイルを自動でフォルダ分けできますか?
Aできます。Get-ChildItem $dir -File | ForEach-Object { ... }で各ファイルを見て、$_.Extension.TrimStart(".")で拡張子名のフォルダを作り、Move-Itemで移動します。記事のスクリプトをそのまま使えば、ダウンロードフォルダなどを拡張子ごとに仕分けられます。
QExtensionにドットが付くのはなぜですか?
A.Extensionの仕様で、.jpgのように先頭にドットが付いた形で返ります。フォルダ名などドットなしで使いたいときは.TrimStart(".")でドットを取り除いてください。拡張子のないファイルでは空文字になります。
Q同じ名前のファイルがあるとどうなりますか?
A移動先に同名ファイルがあるとMove-Itemはエラーになります。Test-Pathで確認し、あればBaseName_1_2と連番を付けてリネームすれば、上書きせずに衝突を回避できます。記事の衝突回避スクリプトを使ってください。
QJPGとjpgが別フォルダに分かれてしまいます。
A拡張子の大文字小文字をそろえていないためです。$_.Extension.TrimStart(".").ToLower()のように.ToLower()を付ければ、.JPG.jpgも同じjpgフォルダにまとまります。
Q実行前に安全に確認するには?
AGet-ChildItem $dir -File | Group-Object Extensionで拡張子ごとの件数を先に確認できます。また、Move-Item-WhatIfを付ければ、実際には移動せずに動きだけを確認できます。初めてのフォルダでは、まず確認してから本実行すると安心です。

まとめ

  • 拡張子は$_.Extensionドット込み)。フォルダ名は.TrimStart(".")で整えます。
  • 拡張子名のフォルダをNew-Itemで作り、Move-Itemで振り分けます。
  • 拡張子なしは_noext大小は.ToLower()で統一します。
  • 同名衝突は連番リネームで回避し、上書き事故を防ぎます。
  • 実行前にGroup-Object Extension-WhatIfで確認すると安心です。

PowerShellなら、散らかったフォルダを拡張子ごとにすっきり自動仕分けできます。「Extensionはドット込み」「同名は連番で回避」の2点さえ押さえれば、ダウンロードフォルダの整理を安全に自動化できます。タスクスケジューラと組み合わせて、定期的に片付く仕組みを作るのもおすすめです。