PowerShell+robocopyで拡張子別にファイルを一括移動する方法

概要

今回はPowerShellで拡張子別にファイルを一括移動する方法について紹介していきます。

というのもGoogle Takeoutから大量にデータをダウンロードすると、画像・動画・JSONがごちゃ混ぜで困りますよね(- -;

手作業で振り分けるのは現実的じゃないので、コマンドでサクッと分類したい所です。

ここではrobocopyを使って、.jpg/.png/.mp4などの拡張子だけを別フォルダへ移動する手順を紹介していきます!

robocopyで拡張子フィルタをかけて移動し、forfilesでフォルダ階層を平らに均し、最後に件数チェックで取りこぼしを確認する、という3ステップの流れです。

これで大量画像の整理が一瞬で終わります。
それではやっていきましょう!

当たり前ですが、移動のコマンドを使用する際はシステムファイルなどを移動するとシステムに影響を与える可能性があります。
注意して、自己責任で実行するようにしてください。

目次

robocopyとforfilesの役割分担

本題に入る前に、今回使う2つのコマンドの守備範囲をざっくり整理しておきます。

  • robocopy(拡張子フィルタ+移動を担当)
    • *.jpgのようなワイルドカードで対象拡張子だけを抜き出し、サブフォルダごと別の場所へ移動/コピーできる。標準搭載で速いのが強み
  • forfiles(フォルダ階層の平坦化を担当)
    • robocopyが引き継いだフォルダ構成を崩し、全ファイルを1つのフォルダ直下にまとめ直すのに使う

robocopyは元のフォルダ構成をそのまま引き継いでコピーするので、最終的にフォルダ直下へ全部まとめたい場合はforfilesとの合わせ技になる、という点だけ先に頭に入れておいてください。

初期状態のフォルダ構成

今回の手順実行前のフォルダ構成は下記のようなイメージです。

手順を実行するにつれて、フォルダ内の状態がどう変化するのか各手順に書いているので、参考にしてみてください。

<初期状態のフォルダ>
  • folder1
    • aaa.png
    • bbb.json
  • folder2
    • ccc.mov
    • ddd.mp4
    • eee.json

手順

再帰的に特定の拡張子のファイルを移動

再帰的にファイルを移動するコマンドを実行します。

実行すると、特定の拡張子のファイルを任意のフォルダに移動することができます。

コマンド

1
robocopy "[移動元パス]" "[移動先パス] " [拡張子1] [拡張子2 ...] /S /MOV /NFL /NDL

例:画像&動画のコピー

1
robocopy ".\google_export\" ".\google_export_photo_only\" *.jpg *.jpeg *.png *.heic *.mp4 *mov *.MOV /S /NFL /NDL

例:画像&動画の移動(個人的には非推奨)

1
robocopy ".\google_export\" ".\google_export_photo_only\" *.jpg *.jpeg *.png *.heic *.mp4 *mov *.MOV /S /MOV /NFL /NDL 
<オプション>
  • /S
    • 再帰的にサブフォルダを探索(空フォルダは無視)
  • /MOV
    • ファイルを移動(コピー後、元ファイルを削除)
    • ※このオプションを無くすと、コピーとなる
  • /NFL
    • 実行中にファイル一覧のログを表示しない
    • ※ このオプションを無くすと、 ログを表示される
  • /NDL
    • 実行中にディレクトリ一覧のログを表示しない
    • ※ このオプションを無くすと、 ログを表示される

移動先パスに画像と動画が配置されることを確認できると思います。

しかしこの方法の場合フォルダ構成も引き継ぐので、
次の手順でフォルダ構成を削除していきます。

<現在のフォルダ内の状態>
  • folder1
    • aaa.png
  • folder2
    • ccc.mov
    • ddd.mp4

ちなみに最初は/MOVを外したコピー版でテスト実行するのがお勧めです(^^b
元ファイルが手元に残っている状態で結果を確認できるので、万が一拡張子の指定をミスしても安全ですよね。
コピーで結果に納得できてから、改めて/MOV付きの移動版を流す、という二段構えにすると事故りにくいです。

フォルダ階層を平坦化してファイルを1つのフォルダにまとめる

robocopyコマンドだとフォルダ構成もコピーされる為、
フォルダ構成を無くし、フォルダ直下にすべてのファイルが配置されるようにします。

念のため新たに作成したフォルダにコピーする形とします。

コマンド

1
forfiles /P "[移動元パス] " /S /M * /C 'cmd /c copy @path "[移動先フルパス]"'

例:画像&動画のコピー

1
forfiles /P ".\google_export_photo_only\" /S /M * /C 'cmd /c copy @path "C:\Users\user1\google_export_photo_only_comp\"'
<オプション>
  • /P
    • 検索フォルダを指定
  • /S
    • サブフォルダをすべて含めて再帰検索
  • /M
    • 移動対象ファイル名を指定
    • ※今回は*なので全部のファイルが対象
  • /C
    • コマンドを実行
    • ※今回はファイルをコピーするコマンドを実行
  • @path
    • 探査で発見されたファイルパス
<現在のフォルダ内の状態>
  • aaa.png
  • ccc.mov
  • ddd.mp4

このようにfolder1やfolder2といった階層が消えて、すべてのファイルが移動先フォルダの直下に並びます。

forfilesは異なるサブフォルダに同名ファイルがあると、後から来たファイルで上書きされてしまいます。
平坦化する前に、同名ファイルが無いかだけ気にしておくと安心です。

ファイルが移動できたか確認

すべてのファイルが正常に移動できたか確認するために、移動元のファイル数と移動先のファイル数が一致していることを確認します

コマンド

1
(Get-ChildItem "[移動元パス or 移動先パス ].\google_export\" -Recurse -Include [拡張子1] [拡張子2 ...] -File).Count

例:画像&動画の場合

1
2
3
4
5

-- 移動元
(Get-ChildItem ".\google_export\" -Recurse -Include *.jpg,*.jpeg,*.png,*.heic,*.mp4,*mov,*.MOV -File).Count
-- 移動先
(Get-ChildItem ".\google_export_photo_only_comp\" -Recurse -Include *.jpg,*.jpeg,*.png,*.heic,*.mp4,*mov,*.MOV -File).Count

Get-ChildItem-Includeにはカンマ区切りで複数の拡張子をまとめて渡せるので、画像と動画をひとまとめに数えられます。
-Fileを付けるとフォルダを除いてファイルだけをカウントしてくれるので、件数がブレにくいです。
-Recurse-Filterの基本的な使い方はget-childitemの-Recurse / -Filterの基本🔗 shinpinoshi.comでまとめているので、合わせて確認してみてください。

ここで件数に差異がある場合、重複ファイルが存在する可能性が高いため、確認する。

大事なファイルが消えないようにするために念のため確認しておきましょう!

よくあるつまずきポイント

最後に、自分が実際にハマった所をいくつか共有しておきます。

  • robocopyの終了コードは0以外でも正常なことがある
    • robocopyはファイルをコピーすると終了コード1を返す仕様で、これはエラーではありません。8以上で本当の失敗、と覚えておくと慌てずに済みます
  • ワイルドカードの大文字小文字に注意
    • *.MOV*movのように、想定する表記ゆれを拡張子リストに足しておくと取りこぼしが減ります
  • 移動先フォルダは事前に作っておく
    • パスのタイプミスで意図しない場所に散らばると後始末が大変なので、まず空フォルダを用意してから流すのが安全です

迷ったらまずコピーで挙動を確かめ、件数チェックで一致を見てから移動に切り替える。
この流れさえ守れば、大量ファイルでも安全に振り分けられます。

締め

これでファイルの移動ができたと思います。

コマンド一つ一つは難しいですが、手動でやるより圧倒的に楽に早くできると思います。

こんな感じの便利なコマンドを自分のサイトでは色々紹介しています。
良ければ他の記事も見ていってください!

以上となります。
大事なファイルを安全にお引越ししましょう!
それではお疲れさまでした!