複数ファイルの拡張子を一度に変更する|for文 + mv / rename / find で確実に動く実務テクニック

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Linuxtips, ディレクトリ・ファイル操作 > 複数ファイルの拡張子を一度に変更する|for文 + mv / rename / find で確実に動く実務テクニック

この記事のポイント

・複数ファイルの拡張子を一度に変更するなら、for文 + mv の組み合わせがどのLinuxでも確実に動く
・renameコマンドはディストリビューションごとに「Perl版」と「util-linux版」があり書式が違うので要注意
・findコマンドと組み合わせるとサブディレクトリ配下も再帰的に変更できる
・誤った一括変更を防ぐには、まず echo を付けて変更内容をプレビューする運用が安全

同じ拡張子のファイルを別の拡張子に一度に変更する場合には、シェルスクリプトで変更可能です。 例えば、「html」という拡張子のファイルを「htm」にする場合にはfor文を使用して下記の様に実行します。 [pakira@Tiger ~]$ touch index.html ←テスト用のhtmlファイルを作成します。 [pakira@Tiger ~]$ touch kouza.html [pakira@Tiger ~]$ touch profile.html [pakira@Tiger ~]$ touch campany.html [pakira@Tiger ~]$ ls  ←テスト用ファイルを表示します。 campany.html index.html kouza.html profile.html [pakira@Tiger ~]$ for name in *.html; do ←拡張子を「.html」から「.htm」に変更します。 > mv $name ${name%.html}.htm; > done [pakira@Tiger ~]$ ls  ←ファイルの拡張子を確認します。 campany.htm index.htm kouza.htm profile.htm
「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

for文 + mv:どのLinuxでも動く「複数ファイルの拡張子を一度に変更する」基本形

冒頭の例で示したfor文 + mvの組み合わせは、bash・dash・zshなど主要なシェルすべてで動作する最も汎用的な方法です。書式の核は ${name%.html}.htm のパラメータ展開で、「変数 name の末尾から .html を取り除いた残り + .htm」という意味です。 [user@host ~]$ for name in *.txt; do mv "$name" "${name%.txt}.md"; done ポイントは "$name" を必ずダブルクォートで囲むことです。スペースを含むファイル名や日本語ファイル名でも、クォートさえ付けておけば事故になりません。さらに ${name%.txt} の % は「末尾から最短一致で削除」を意味するため、ファイル名の途中に .txt が含まれていても誤動作しません。 mv コマンドは「移動」と「リネーム」の両方を担うコマンドで、同一ディレクトリ内で名前だけ変える操作はリネームとして扱われます。物理的にデータが書き直されるわけではないので、数千ファイルでも一瞬で終わります。

renameコマンド(Perl版)で複数ファイルの拡張子を変更する

Debian系(Ubuntu等)には、Perlで書かれた高機能なrenameコマンドが標準で入っていることが多くあります。正規表現で変換ルールを書けるので、拡張子の変更も非常にコンパクトに記述できます。 [user@host ~]$ rename 's/\.html$/\.htm/' *.html s/\.html$/\.htm/ は sed と同じ置換構文で、行末(ファイル名末尾)の .html を .htm に置き換えます。-n オプションを付けると、実際にはリネームせず「どう変わる予定か」を表示するドライランができます。 [user@host ~]$ rename -n 's/\.html$/\.htm/' *.html rename(campany.html, campany.htm) rename(index.html, index.htm) rename(kouza.html, kouza.htm) rename(profile.html, profile.htm) ドライランで確認 → 問題なければ -n を外して本実行、という運用にすると事故率が大きく下がります。

renameコマンド(util-linux版)の書式と注意点

一方、RHEL系(CentOS、AlmaLinux、Rocky Linux、Fedora)に標準で入っているのは util-linux 版の rename です。Perl版とは書式が全く違うので、同じ感覚で叩くと意図しない結果になります。 [user@host ~]$ rename .html .htm *.html ←util-linux版の書式 util-linux版の rename は単純な文字列置換で、正規表現は使えません。書式は「rename 置換前 置換後 対象ファイル」の3引数です。Perl版を期待してスラッシュ区切りの正規表現を書くと、リテラル文字列としてファイル名にスラッシュが入る形になり、不可解な動作になります。 どちらの rename が入っているかは、次のコマンドで確認できます。 [user@host ~]$ rename --version rename from util-linux 2.37.4 ←util-linux版 または /usr/bin/rename - renames multiple files ←Perl版(man rename の冒頭) ディストリビューションを跨いで使うシェルスクリプトでは、rename ではなく for文 + mv で書く方が圧倒的に移植性が高くなります。

find コマンドと組み合わせてサブディレクトリも再帰的に変更する

カレントディレクトリだけでなく、サブディレクトリ配下のファイルも一括で拡張子を変更したい場合は find と組み合わせます。 [user@host ~]$ find . -name "*.html" -exec sh -c 'mv "$0" "${0%.html}.htm"' {} \; find . -name "*.html" でカレント以下のすべての .html を列挙し、-exec sh -c '...' {} \; でファイル1つずつにmvを適用します。$0 は -exec で渡される位置パラメータで、find の出力した1ファイル名がそのまま入ります。 サブディレクトリが深い場合は、xargs を使った方が高速です。 [user@host ~]$ find . -name "*.html" -print0 | xargs -0 -I{} sh -c 'mv "{}" "${1%.html}.htm"' _ {} -print0 と -0 でNULL区切りにすると、ファイル名にスペースや改行が含まれていても正しく扱えます。バックアップディレクトリのHTMLを全部 .htm に変更したいときなどに重宝します。

echo を挟んで変更内容を事前にプレビューする安全運用

「複数ファイルを一度に変更する」操作は便利な反面、間違ったら一括で壊れます。そこで、まず mv の前に echo を付けて変更プレビューを取るのが現場の定番です。 [user@host ~]$ for name in *.html; do echo mv "$name" "${name%.html}.htm"; done mv campany.html campany.htm mv index.html index.htm mv kouza.html kouza.htm mv profile.html profile.htm 期待通りの出力になっていることを目視確認してから、echo を外して再実行します。これを習慣にしておくと、思いついた瞬間にEnterを押して大事故、という典型パターンをほぼ防げます。 renameコマンド(Perl版)も -n オプションでドライランできるので、考え方は同じです。「破壊的なコマンドの前は必ずプレビュー」を徹底するのが、複数ファイル操作の鉄則です。

同名ファイルが既にある場合の上書き対策

複数ファイルの拡張子を一度に変更するとき、変更先のファイル名が既に存在していると mv は黙って上書きしてしまいます。これは Linux 標準のmvの挙動で、確認なしに既存ファイルを潰します。 これを防ぐには、mv コマンドに -n(上書きしない)または -i(対話確認)オプションを付けます。 [user@host ~]$ for name in *.html; do mv -n "$name" "${name%.html}.htm"; done -n を付けると、変更先のファイルが既にある場合はそのままスキップされます。スクリプト化する場合は -n、対話的に使う場合は -i が無難です。aliasで mv='mv -i' を設定しておくと、ホームディレクトリ全体で常に確認が走るようになり、誤上書き事故を仕組みで防げます。

basename + parameter展開:拡張子を「足す」「外す」も自在

${変数%.拡張子} の構文は、拡張子の置換だけでなく「拡張子の削除」や「全く別の拡張子の追加」にも応用できます。複数ファイルの拡張子を一度に変更する作業のバリエーションを一覧にすると、用途の広さがイメージしやすくなります。 [user@host ~]$ for f in *.bak; do mv "$f" "${f%.bak}"; done ←.bakを外す [user@host ~]$ for f in *; do mv "$f" "$f.old"; done ←全ファイルに.oldを付ける [user@host ~]$ for f in *.JPG; do mv "$f" "${f%.JPG}.jpg"; done ←大文字拡張子を小文字に統一 最後の例は、デジカメ等から取り出した大文字 .JPG を Web向けの小文字 .jpg に揃える定番技です。同じ要領で .HTM → .htm、.TXT → .txt の正規化もワンライナーで終わります。

【重要】複数ファイルの拡張子を一度に変更する時の落とし穴

便利な一括変更ですが、現場でよく見るトラブルパターンを4つ紹介します。 1つ目は「カレントディレクトリを間違えて全く別の場所のファイルを変更してしまう」事故です。cd で移動した直後に pwd で現在地を確認する習慣を付けるか、変更コマンドの直前に ls *.html で対象一覧を必ず目視するのが鉄則です。 2つ目は「シンボリックリンクの実体まで変更してしまう」ケースです。mv はシンボリックリンクそのものをリネームするだけで実体には触れませんが、シンボリックリンクが指す先のファイル名と一括変更後の名前がずれて、リンク切れになることがあります。lsof や readlink で参照関係を確認してから実行します。 3つ目は「Gitリポジトリ内で git mv を使わずに mv だけ実行してしまう」パターンです。普通の mv だと履歴が「削除+追加」として記録され、git log --follow が効きにくくなります。Git管理下で複数ファイルの拡張子を変更するときは git mv で同じ操作をするか、まとめてgit addし直します。 4つ目は「macOSの大文字小文字を区別しないファイルシステムでハマる」ケースです。HFS+やAPFS(デフォルト設定)では .JPG と .jpg が同一ファイルとして扱われ、mv で衝突します。一旦別名を経由する2段階リネームが必要です。LinuxからNFSやsmbfsでmacOSのボリュームを触る場合も同じ罠があります。

「No such file or directory」「Argument list too long」が出た時の対処

複数ファイルの拡張子を一度に変更する作業で頻発する2大エラーへの対処法をまとめます。 「No such file or directory」は、対象パターン *.html にマッチするファイルが1つもないときに起きます。bashの nullglob オプションを設定しておくと、マッチゼロのときはfor文自体が回らなくなります。 [user@host ~]$ shopt -s nullglob [user@host ~]$ for name in *.html; do mv "$name" "${name%.html}.htm"; done 「Argument list too long」は、対象ファイルが数万件あって ARG_MAX を超えたときのエラーです。このときは find + xargs -0 を使うと、xargs が自動で分割実行してくれるので問題なく処理できます。 [user@host ~]$ find . -maxdepth 1 -name "*.html" -print0 | xargs -0 -I{} sh -c 'mv "{}" "${1%.html}.htm"' _ {} 数万〜数十万ファイルを一括処理する現場では、最初から find + xargs の構文で書く方が安全です。

本記事のまとめ:複数ファイルの拡張子を一度に変更するコマンド早見表

最後に、複数ファイルの拡張子を一度に変更する各種コマンドを一覧にまとめます。
目的 コマンド例
どのLinuxでも確実に動く基本形 for name in *.html; do mv "$name" "${name%.html}.htm"; done
事前プレビュー(破壊なし) for name in *.html; do echo mv "$name" "${name%.html}.htm"; done
Perl版 rename(Debian/Ubuntu) rename 's/\.html$/\.htm/' *.html
Perl版 rename のドライラン rename -n 's/\.html$/\.htm/' *.html
util-linux版 rename(RHEL/AlmaLinux) rename .html .htm *.html
サブディレクトリも再帰的に変更 find . -name "*.html" -exec sh -c 'mv "$0" "${0%.html}.htm"' {} \;
大量ファイル(数万件)の処理 find . -name "*.html" -print0 | xargs -0 -I{} sh -c 'mv "{}" "${1%.html}.htm"' _ {}
上書き事故を防ぐ mv -n または mv -i を使う
複数ファイルの拡張子を一度に変更する操作は、「for文 + mv」を中心に押さえれば9割の現場で困りません。Debian系のPerl版 rename、RHEL系の util-linux 版 rename はディストリ依存があるため、移植性が必要なスクリプトでは避けるのが安全策です。echo によるプレビュー → 本実行の2段階運用を徹底すれば、誤った一括変更による事故もほぼ防げます。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。