ファイル・フォルダの名前を一括変更したい!【便利コマンド】

Blog-Thumbnail_Linux-01

はじめに

この記事を読むとできること

Windows / Mac / Linux 、どの OS でもファイル名・フォルダ名を一括で変更できるようになります。

file-directory-rename「ファイル名・フォルダ名を一括変更」
file-directory-rename「ファイル名・フォルダ名を一括変更」

上記の画像では、文字列 xxx を 001 に変更しています
他にも、以下のような変更が可能となります。

  • ファイル名に “0001” から始まる連番を一括で付与
    • 例:
      • before: image.jpg
      • after: image-0001.jpg, image-0002.jpg, …
  • ファイル名の末尾に、フォルダと同じ末尾を一括で付与
    • 例:
      • before: project-2024-01/data.csv
      • after: project-2024-01/data-2024-01.csv

ファイル/フォルダ名の一括変更の必要性

ファイルやフォルダは情報の基本単位です。
それらの命名は情報の管理の観点で非常に重要です。

これを読んでいる人は、おそらく情報管理やプロジェクトマネジメント系の業務をされる方だと予想してます。
(もしくは几帳面な方でファイル・フォルダ構成を美しくしたい方かと)

しかし、ファイル・フォルダの構成は時折見直しが必要になりますよね。
例えば、

  • プロジェクトの次フェーズ突入
  • チームメンバーの変更
  • 次年度への突入
  • ファイル数の急増による桁数の増加

等々、色々考えられます。
上記のように、命名規則の変更や、より効率的な命名体系への更新が必要になることがよくあります。
ここで一括変更が必須スキルとなります。

ファイル・フォルダ数が数個なら手動で実行する方が早いかもしれません。
しかし、上記のようなケースでは数百、数千等、桁が違います。
一括変更は、手動で一つひとつ変更する煩わしさと時間の浪費を回避し、名前の一貫性と効率を保ちながら大量のファイルやフォルダを迅速に更新することを可能にします。
この記事では、Windows / Mac / Linux で利用可能なコマンドをお伝えします。

なぜ画面操作ではなくコマンドなのか

かっこつけたいからではないです。(正直 CLI の方がかっこいいとは思っていますが)

ちなみに、Windows のデスクトップのようにアイコン等をクリックして操作する UI を、GUI (Graphical User Interface) といいます。
一方で、CLI (Command Line Interface) とは、Windows のコマンドプロンプト、Mac のターミナルのように、コマンドで操作する UI の事を指します。
また、GUI の文脈で「フォルダ」と呼ばれるものは、CUI の文脈では基本的に「ディレクトリ」と表現されます。
そのため、今後ちょくちょく「ディレクトリ」と表現することがありますが、「あぁ、フォルダのことね」と読み替えてください。

GUI での操作は直感的でわかりやすいですが、いくつかの要素で CLI に劣ります。
特に、ファイル・フォルダの名前の一括変更のような操作は CLI の方が圧倒的に強力です。
その理由として、以下のポイントが挙げられます。 

カスタマイズ性と柔軟性

まず、前提として CLI は非常にカスタマイズ性に富んでおります。
例えば、特定のニーズに合わせて精密な操作が可能で、一括変更の処理を独自の要件に合わせて細かく調整することが可能です。

  • フォルダのみを指定して一括変更
  • ファイルのみを指定して一括変更
  • ファイル名/フォルダ名の特定の文字列 abc を 123 に置き換える
  • ファイル名/フォルダ名の先頭/末尾に特定の文字列を付加する
  • …etc

OSの独立性

次に、Linux、Mac、Windows など、様々な OS で共通のコマンドが利用可能です(Windows は Git Bash や WSL の利用など)。

ビジネスシーンでは、OS に依存しない操作は非常に重要ですよね。
まず、異なるシステム間で作業を行う際の学習コストを大幅に減らします。
また、システムの移行時にもオペレーションの変更が発生しません。
他にも、メンバーが端末を変更した際にマニュアル整備する必要もありません。

逆に、GUI での操作に依存している場合は、Windows アップデート等によって GUI が変わって操作がわからなくなることもあります。

効率的な作業の実現

さいごに、CLI に慣れている方ならわかるかと思いますが、マウス操作による GUI ツールを使用するよりもはるかに迅速に作業を行うことができます。

イメージとしては、コピーペーストする際に
マウス右クリックして、コピーして、マウス右クリックして、張り付けする
よりも、
「Ctrl + C でコピーして、Ctrl + V で張り付けする」
方が早いよね。という話と同じです。

特に、大量のファイルを扱う場合や複雑なパターンに基づいて名前を変更する場合に、時間と労力を大幅に削減することができます。

ファイル/フォルダ名を一括で変更するコマンド 5 選

ここでは、ファイルやフォルダ名を一括で変更するためによく使用される 5 つのコマンドを紹介します。

for + mv

for ループと mv コマンドを組み合わせることで、特定のパターンにマッチするファイルやディレクトリ名を順番に変更できます。
この方法は基本的かつ直感的に利用でき、特に、単純なパターンの変更に適しています。

実行例

for file in oldname*; do
    mv "$file" "newprefix${file#oldname}"
done
  • 処理の流れ:
    • oldname* にマッチする全てのファイルに対してループ処理を実行
  • mv コマンド:
    • oldname で始まるファイル名を newprefix に変更
    • $file は現在のファイル名
    • ${file#oldname} は oldname を削除した後のファイル名

メリット

  • Bash の基本コマンドのため、多くの Unix 系システムで簡単に利用できる。
  • シンプルでわかりやすい。基本的なループと条件を理解していれば使用可能。

デメリット

  • 複雑なパターンや大量のファイルに対する処理は効率が低下する。
  • 再帰的な操作やより複雑な条件が必要な場合は不向き。

ユースケース

  • カレントディレクトリ内のファイルやディレクトリ名を簡単なパターンに基づいて変更したい場合。
  • ファイルの数が少なく、特定のパターンに基づく変更が必要な場合。

find + sh(mv)

find コマンドは、指定されたパターンにマッチするファイルやディレクトリをサブディレクトリ含めて検索することができます。
検索結果を sh (または他のシェル)スクリプトに渡し、mv コマンドで名前を一括変更します。
この方法は特にサブディレクトリを含む複雑なディレクトリ構造での名前変更に適しています。

実行例

find . -type d -name 'oldname*' -exec sh -c 'mv "$1" "${1/oldname/newname}"' _ {} \;
  • 処理の流れ:
    • カレントディレクトリ配下で oldname* にマッチする全てのディレクトリを検索
  • -exec オプション:
    • 検索にマッチした各ディレクトリに対して指定されたコマンドを実行
    • ここではシェルスクリプトを実行し、mv コマンドでディレクトリ名を変更
    • ${1/oldname/newname} は第一引数($1、つまり検索されたディレクトリ名)中の oldname を newname に置換します。

メリット

  • ディレクトリの深い階層にわたってファイルやディレクトリを検索し、一括で名前変更ができる。
  • 強力な検索オプションと柔軟性を提供。

デメリット

  • コマンドが複雑になる傾向がある。特に、複数のオプションや-execの引数を正確に理解する必要がある。
  • 他の方法と比べて少し非直感的かもしれない。

ユースケース

  • サブディレクトリを含む複雑なフォルダ構造全体での名前変更が必要な場合。
  • 特定の条件(ファイルサイズ、変更日など)に基づいてファイルやディレクトリを選択して名前を変更する場合。

rename

rename は、Perl スクリプトの一種で、正規表現を使って複数のファイル名を一括で変更できます。
このコマンドは非常に強力で、複雑な名前のパターンや条件にマッチする変更が必要な場合に特に役立ちます。

実行例

rename 's/oldname/newname/' oldname*
  • 処理の流れ:
    • oldname* にマッチする全てのファイルの名前を newname に変更
  • rename コマンド:
    • ‘s/oldname/newname/’ というパターンで、oldname を newname に置換

メリット

  • 正規表現を利用して、複雑なパターンや条件に基づいてファイル名を一括変更できる。
  • 大量のファイルに対して迅速に変更を適用できる。

デメリット

  • Perl に依存しているため、すべての環境にはインストールされていない場合がある。
  • 正規表現は非常に強力だが、ミスが起こりやすいので注意。

ユースケース

  • 大規模なファイルシステムで複雑なパターンに基づいて名前を一括で変更したい場合。
  • 一貫性のない命名規則を整理し、新しい命名規則に一括で変更する必要がある場合。

for + awk + mv

awk はテキスト処理に特化した言語で、for ループと組み合わせることでファイル名のパターンを複雑に操作し、それに応じて mv で名前を変更できます。
特に、名前の変更が数値のインクリメントや複雑なテキスト処理を伴う場合に適しています。

実行例

for file in oldname*; do
    newname=$(echo "$file" | awk '{print "newprefix" substr($0,9)}')
    mv "$file" "$newname"
done
  • 処理の流れ:
    • oldname* にマッチする全てのファイルに対してループ処理
  • awk コマンド:
    • substr($0,9) はファイル名の9文字目から最後までの部分文字列を抽出
    • その文字列に newprefix を前置して新しいファイル名を作成し、mv コマンドで名前を変更

メリット

  • awk の強力なテキスト処理機能を利用して、複雑な文字列操作やパターンマッチングを行える。
  • 数値操作や条件分岐など、高度なテキスト処理が可能。

デメリット

  • コマンドが複雑で、awk の文法に慣れている必要がある。
  • シンプルな名前変更作業には過剰な機能を提供する場合がある。

ユースケース

  • 名前の変更に複雑な数値計算や文字列操作が必要な場合。
  • 一括変更ルールが複雑で、特定のパターンや条件に従う必要がある場合。

for + sed + mv

sed はストリームエディタで、正規表現を使ったテキストの検索と置換に優れています。
for ループと組み合わせて、ファイル名の検出と置換を行い、最終的に mv で名前を変更します。
この方法は、特に正規表現を用いた複雑なパターン置換に適しています。

例:

for file in oldname*; do
    newname=$(echo "$file" | sed 's/oldname/newname/')
    mv "$file" "$newname"
done
  • 処理の流れ:
    • oldname* にマッチする全てのファイルに対してループ処理
  • sedコマンド:
    • sed ‘s/oldname/newname/’ は、ファイル名内の oldname を newname に置換
    • 結果として得られる新しいファイル名を mv コマンドで適用

メリット

  • sed の正規表現による検索・置換を使用して、複雑なパターンに基づいてファイル名を変更できる。
  • コマンドは直感的で、多くの Unix 系システムで広く利用できる。

デメリット

  • 正規表現の構文に慣れる必要がある。
  • 大規模な変更作業には、実行時間が長くなる可能性がある。

ユースケース

  • 正規表現を使って複雑なパターンや条件に基づいた名前変更が必要な場合。
  • ファイル名の一部を特定のパターンで置換する必要がある場合。

コマンド実行する前に注意点

以上のコマンドは、それぞれ異なる状況や要件に応じて使い分けると良いかと思います。
なお、使用する際は、例のごとく実行前に十分なテストを実施してください。

また、重要なデータの場合は、変更を加える前にバックアップを取りましょう。
もしもミスした場合、ぐちゃぐちゃになってしまった構成を元に戻すのは骨が折れるので。

実際にファイル・ディレクトリ名を一括変更してみよう

この章では、実際にファイル・ディレクトリ名を一括変更する例を記述します。
前述の for + mv を実行してみようと思います。
以下の OS, Shell, フォルダ構造の場合を例に、コマンドを実際にやってみましょう。

$ printenv OS
Windows_NT
$ printenv SHELL
/usr/bin/bash
$ tree .
.
|-- project-xxx-01
|   |-- data-xxx.csv
|   |-- image-xxx.jpg
|   `-- script-xxx.py
|-- project-xxx-02
|   |-- data-xxx.csv
|   |-- image-xxx.jpg
|   `-- script-xxx.py
`-- project-xxx-03
    |-- data-xxx.csv
    |-- image-xxx.jpg
    `-- script-xxx.py

3 directories, 9 files

最終的に、以下のような構成にしましょう。

.
|-- project-2024-01
|   |-- data-2024.csv
|   |-- image-0001.jpg
|   `-- script-2024-01.py
|-- project-2024-02
|   |-- data-2024.csv
|   |-- image-0002.jpg
|   `-- script-2024-02.py
`-- project-2024-03
    |-- data-2024.csv
    |-- image-0003.jpg
    `-- script-2024-03.py

フォルダ名に含まれる文字列 “xxx” を “2024” に一括変更

まず、各フォルダ “project-xxx-01″ を、”project-2024-01” のように一括変更しましょう。

$ for dir in project-xxx-*; do
    mv "$dir" "${dir/xxx/2024}";
done
$ tree .
.
|-- project-2024-01
|   |-- data-xxx.csv
|   |-- image-xxx.jpg
|   `-- script-xxx.py
|-- project-2024-02
|   |-- data-xxx.csv
|   |-- image-xxx.jpg
|   `-- script-xxx.py
`-- project-2024-03
    |-- data-xxx.csv
    |-- image-xxx.jpg
    `-- script-xxx.py

3 directories, 9 files
  • for dir in project-xxx-; do … done:
    • project-xxx- にマッチする全てのディレクトリに対してループ処理
  • mv “$dir” “${dir/xxx/2024}”:
    • 各ディレクトリ名の xxx 部分を 2024 に置換
    • その結果を使ってディレクトリ名を変更

各フォルダ内のファイル名に含まれる “xxx” を “2024” に一括変更

次に、ファイル “data-xxx.csv” を、”data-2024.csv” に一括変更しましょう。

$ for dir in project-2024-*; do
    mv "$dir/data-xxx.csv" "$dir/data-2024.csv";
done
$ tree .
.
|-- project-2024-01
|   |-- data-2024.csv
|   |-- image-xxx.jpg
|   `-- script-xxx.py
|-- project-2024-02
|   |-- data-2024.csv
|   |-- image-xxx.jpg
|   `-- script-xxx.py
`-- project-2024-03
    |-- data-2024.csv
    |-- image-xxx.jpg
    `-- script-xxx.py

3 directories, 9 files
  • for dir in project-2024-; do … done:
    • project-2024- にマッチする全てのディレクトリに対してループ処理
  • mv “$dir/data-xxx.csv” “$dir/data-2024.csv”:
    • 各ディレクトリ内の data-xxx.csv ファイルの名前を data-2024.csv に変更

各フォルダ内のファイル名に含まれる “xxx” を “0001” から始まる連番に一括変更

次に、ファイル “image-xxx.jpg” を、”image-0001.jpg” から始まる連番に一括変更しましょう。

$ counter=1
for dir in project-2024-*; do
    num=$(printf "%04d" $counter)
    mv "$dir/image-xxx.jpg" "$dir/image-$num.jpg"
    ((counter++))
done
$ tree .
.
|-- project-2024-01
|   |-- data-2024.csv
|   |-- image-0001.jpg
|   `-- script-xxx.py
|-- project-2024-02
|   |-- data-2024.csv
|   |-- image-0002.jpg
|   `-- script-xxx.py
`-- project-2024-03
    |-- data-2024.csv
    |-- image-0003.jpg
    `-- script-xxx.py

3 directories, 9 files
  • counter=1:
    • カウンタを1から開始
  • for dir in project-2024-; do … done:
    • project-2024- にマッチする全てのディレクトリに対してループ処理
  • num=$(printf “%04d” $counter):
    • カウンタを4桁のゼロ埋め数字に変換(例:0001、0002)
  • mv “$dir/image-xxx.jpg” “$dir/image-$num.jpg”:
    • 各ディレクトリ内の image-xxx.jpg ファイルの名前を image-0001.jpg, image-0002.jpg … のように連番付きの名前に変更
  • ((counter++)):
    • ループごとにカウンタを1増加

各フォルダ内のファイル名に含まれる “xxx” をフォルダと同じ末尾に一括変更

さいごに、ファイル “script-xxx.py” を、”script-2024-01.py” のように、親ディレクトリ(フォルダ)と同じ末尾に一括変更しましょう。

$ for dir in project-2024-*; do
    num=$(basename "$dir" | cut -d'-' -f3)
    mv "$dir/script-xxx.py" "$dir/script-2024-$num.py"
done

$ tree .
.
|-- project-2024-01
|   |-- data-2024.csv
|   |-- image-0001.jpg
|   `-- script-2024-01.py
|-- project-2024-02
|   |-- data-2024.csv
|   |-- image-0002.jpg
|   `-- script-2024-02.py
`-- project-2024-03
    |-- data-2024.csv
    |-- image-0003.jpg
    `-- script-2024-03.py

3 directories, 9 files
  • for dir in project-2024-; do … done:
    • project-2024- にマッチする全てのディレクトリに対してループ処理
  • num=$(basename “$dir” | cut -d’-‘ -f3):
    • basename “$dir”:
      • フルパスが与えられたディレクトリ名から、そのベース名(最後の/以降の部分)を取得(例えば、/path/to/project-2024-01 のベース名は project-2024-01)
    • cut -d’-‘ -f3:
      • -d は区切り文字(delimiter)を指定するオプション。ここでは、-(ハイフン)を区切り文字として指定
      • -f はフィールド番号を指定するオプション。3 は 3 番目のフィールド、つまり 3 番目の区切り文字で区切られた部分を抽出(例えば、project-2024-01 の場合は 01)
  • mv “$dir/script-xxx.py” “$dir/script-2024-$num.py”:
    • 各ディレクトリ内の script-xxx.py ファイルの名前を script-2024-01.py, script-2024-02.py … のようにディレクトリの末尾番号に一致する名前に変更

さいごに

先程紹介した例以外にも、様々なユースケースがあると思います。
ただ、コマンドは自由にカスタマイズが可能なので、ファイル名の一括変更なら大体なんでも対応できるかと思います。
とにかく、「CLI なら様々なケースにおいて柔軟に対応が可能そう」ということが伝われば幸いです。

さいごに、ざっくりと今回のまとめです。

コマンド選択の基準

まず、一括変更作業に適切なコマンドを選択する際には、以下の基準を考慮しましょう。

  1. 複雑さとスキルレベル:
    • 簡単な名前変更であれば、for + mv や for + sed + mv を使おう
    • より複雑なパターンや条件が必要な場合は、awk や rename を使おう
    • 自身のスキルレベルと快適さも考慮し、適切なツールを選択しよう
  2. 変更の範囲:
    • ディレクトリの階層が深い場合や、特定の条件に基づいてファイルを選択する必要がある場合は find + sh(mv) を使おう
  3. 利用可能なツールと環境:
    • 使用しているシステムやシェル、利用可能なツールに基づいてコマンドを選択しよう
    • 特定のコマンドが利用できない場合は、利用可能な代替コマンドを検討しよう
  4. 安全性と信頼性:
    • 変更前に小規模なテストをしよう
    • バックアップを取得しよう

コマンド 5 選まとめ

# 1. for + mv
for file in oldname*; do
    mv "$file" "newprefix${file#oldname}"
done

# 2. find + sh(mv)
find . -type d -name 'oldname*' -exec sh -c 'mv "$1" "${1/oldname/newname}"' _ {} \;

# 3. rename
rename 's/oldname/newname/' oldname*

# 4. for + awk + mv
for file in oldname*; do
    newname=$(echo "$file" | awk '{print "newprefix" substr($0,9)}')
    mv "$file" "$newname"
done

# 5. for + sed + mv
for file in oldname*; do
    newname=$(echo "$file" | sed 's/oldname/newname/')
    mv "$file" "$newname"
done

コマンド for + mv 実行例まとめ

# フォルダ名に含まれる文字列 “xxx” を “2024” に一括変更
$ for dir in project-xxx-*; do
    mv "$dir" "${dir/xxx/2024}";
done

# 各フォルダ内のファイル名に含まれる “xxx” を “2024” に一括変更
$ for dir in project-2024-*; do
    mv "$dir/data-xxx.csv" "$dir/data-2024.csv";
done

# 各フォルダ内のファイル名に含まれる “xxx” を “0001” から始まる連番に一括変更
$ counter=1
for dir in project-2024-*; do
    num=$(printf "%04d" $counter)
    mv "$dir/image-xxx.jpg" "$dir/image-$num.jpg"
    ((counter++))
done

# 各フォルダ内のファイル名に含まれる “xxx” をフォルダと同じ末尾に一括変更
$ for dir in project-2024-*; do
    num=$(basename "$dir" | cut -d'-' -f3)
    mv "$dir/script-xxx.py" "$dir/script-2024-$num.py"
done

以上です。

コメント