Windows PowerShellを使って複雑なパターンのファイル名変更を行うTech TIPS

多数のファイルの名前をまとめて変更したい場合、CUIのツールを使うと素早く作業できる。だがコマンドプロンプトのrenコマンドでは、名前の長さが変わるような変更が難しいなど、制約が多い。PowerShellなら、Rename-Itemコマンドレットと正規表現機能を組み合わせて柔軟なファイル名変更ができる。

» 2014年11月07日 00時00分 公開
[打越浩幸デジタルアドバンテージ]
Tech TIPS
Windows Server Insider


「Tech TIPS」のインデックス

連載目次

対象ソフトウェア:Windows PowerShell



解説

 例えば「新規 Microsoft Word 文書.docx」というファイルがあるとしよう。その名前の一部を変更して「新規案件文書.docx」としたい場合、エクスプローラー上でファイル名をクリックするか、右クリックしてポップアップメニューから[名前の変更]を選んだり、[F2]キーを押して名前を変更する機能を利用すれば簡単である。

 だが対象となるファイルが複数ある場合はこの方法は使えない。エクスプローラーで複数のファイルをまとめて選択し、この状態で[F2]キーを押したり、右クリックして[名前の変更]を行ったりすると、全てのファイル名が例えば「新規案件文書 (1)」「新規案件文書(2)」……のように、「固定的な名前」+「(連番)」という形式に変更されてしまうからだ(TIPS「複数ファイルのファイル名を一括して変換する」参照)。

 ならばコマンドプロンプト上で「ren(rename)」コマンドとワイルドカード指定を組み合わせればよいと思うかもしれないが、これはファイル名の長さが変わらない場合にしか利用できないという(かなり厳しい)制約がある。

C:\Prj\Documents>dir /b  …ファイル名の確認。/bで名前だけを表示させている
新規 Microsoft Word 文書 1.docx  …元のファイル名。末尾の番号だけが異なる
新規 Microsoft Word 文書 2.docx
新規 Microsoft Word 文書 3.docx
新規 Microsoft Word 文書 4.docx
新規 Microsoft Word 文書 5.docx

C:\Prj\Documents>ren "新規 Microsoft Word 文書*.docx" "新規案件文書*.*"  …renコマンドによるファイル名の変更の指定。元のままにしたい部分(変更させたくない部分)を「*」にしてみた

C:\Prj\Documents>dir /b  …変更結果の確認
新規案件文書rosoft Word 文書 1.docx  …なにやら、かなりひどいことになっている
新規案件文書rosoft Word 文書 2.docx
新規案件文書rosoft Word 文書 3.docx
新規案件文書rosoft Word 文書 4.docx
新規案件文書rosoft Word 文書 5.docx

C:\Prj\Documents>



 renコマンドは「ren <旧ファイル名> <新ファイル名>」のように使う。<旧ファイル名>指定にワイルドカード文字(「?」で任意の1文字、「*」で任意の複数文字にマッチ)を使うと複数のファイルにマッチする。<新ファイル名>指定にワイルドカード文字を使うと、その部分は元のまま維持され(「*」は、残りを全部「?」にすることにほぼ相当する)、非ワイルドカード文字の部分は新ファイル名の一部になる。これはWindows OSの前身であるMS-DOSの頃からの仕様である。つまり、<旧ファイル名>中のワイルドカード指定と、<新ファイル名>中のワイルドカード指定は何の関係もないということだ。前者のワイルドカード指定は単に複数のファイルを選択するために使われ、後者は「変更させたくない」文字を指定するために使われる。

 上の例では変更後のファイル名指定を「新規案件文書*.*」としたため、元のファイル名の先頭から6文字が「新規案件文書」になったが、7文字目以降は「*」によって元のファイル名がそのまま維持されている。その結果、「新規案件文書rosoft Word 文書 1.docx」などというファイル名になってしまっている。

 このようにrenコマンドは、基本的には長さ(文字位置)が変わらないようなファイル名変更にしか使えない。「画像*.jpg」→「写真*.jpeg」のような変更には使えるが、「photo*.jpg」→「旅行写真*.jpeg」のような変更には使えないということだ。

 ではファイル名の長さや文字列の場所(位置)が変わるようなファイル名の変更はどうすればよいか? いろいろ方法があるが、PowerShellを使うと簡単だ。本TIPSでは、PowerShellを使って、コマンドプロンプトのrenコマンドでは対応できないような、複雑なパターンのファイル名変更を行う方法を紹介する。

操作方法

●PowerShellと正規表現を使ったファイル名の変更方法

 PowerShellで複雑なパターンのファイル名変更を行うには、次のようなコマンドを実行すればよい。Rename-Itemコマンドの使い方は「Get-Help Rename-Item」で確認できる。

Get-ChildItem <対象ファイル> | Rename-Item -NewName { $_.Name -replace '旧文字列','新文字列' }



 Get-ChildItemは、コマンドプロンプトのdirに相当するコマンドレットだ。これで名前変更の対象となるファイルの一覧を列挙させる。複数のパターンを指定してもよいし、サブフォルダ中のファイルも対象にしたい場合は -r-Recurse)オプションを付ければよい(右上の関連記事参照)。

 対象となるファイル名の一覧が得られたら「Rename-Item -NewName <新ファイル名>」というコマンドを繰り返し実行させる(変更対象のファイル名はパイプ経由で渡される)。この例では<新ファイル名>として「{ $_.Name -replace '旧文字列','新文字列' }」という「(PowerShellの)スクリプトブロック」を渡している。このブロック中では、パイプ経由で渡されたファイル名($_.Name)に対して、-replaceでファイル名中の文字列の一部を置換している。コマンドプロンプトのrenコマンドと違って、PowerShellのRename-Itemコマンドレットでは新ファイル名中にワイルドカードを含めることはできず、このように必ず完全なファイル名を-NewNameで渡す必要がある。

 「-replace '旧文字列','新文字列'」は文字列の置換を行う演算子だ。「'旧文字列'」には置換前の名前(の一部)に合致する正規表現を、「'新文字列'」には置換後の文字列をそれぞれ指定する。例えば「.txt」を「.text」に変えたい場合は「-replace '\.txt','.text'」のようにする。旧文字列側には「\」を付けておかないと、その次の「.」がワイルドカード文字として扱われ、ピリオド以外の文字にもマッチしてしまうので注意していただきたい(より厳密には「-replace '\.txt$','.text'」としないとファイル名の途中の「.txt」に合致してしまう可能性がある)。

 「'旧文字列'」に含まれる文字列の一部を「'新文字列'」中で引用したい場合は、正規表現のグループ機能を使う。「(」と「)」で囲まれた部分が「$1」「$2」「$3」……のように参照できるので、これを使って新しいファイル名を合成すればよい。例えば日付をベースにした「applog141102.log」というファイルがあるなら、「-replace 'applog([0-9][0-9])([0-9][0-9])([0-9][0-9])','applicationlog-20$1-$2-$3'」とすれば、「applicationlog-2014-11-02.log」という形に変換できる(数字1桁を指定する「[0-9]」は「\d」とも記述できるが、これだと全角数字も合致するので注意)。なお、「"$1"」のようにダブルクォート記号で囲むとPowerShellの変数と見なされて先に展開されてしまう。正規表現の置き換えパターンとして認識させるためには、「'$1'」のようにシングルクォートで囲むこと。

●正規表現の表記方法

 正規表現の詳しい記述方法や置換文字列の指定方法については、以下のリンク先を参照していただきたい。

●PowerShellによるファイル名の変更例

 それではPowerShellで「新規 Microsoft Word 文書*.docx」を「新規案件文書*.docx」に変換してみよう。PowerShellのコンソールを起動して、実行してみる。

PS C:\Prj\Documents> dir -name  …ファイル名の確認(dirはGet-ChildItemのエイリアス)。-nameでファイル名だけを表示させている
新規 Microsoft Word 文書 1.docx  …元のファイル名。末尾の番号だけが異なる
新規 Microsoft Word 文書 2.docx
新規 Microsoft Word 文書 3.docx
新規 Microsoft Word 文書 4.docx
新規 Microsoft Word 文書 5.docx
PS C:\Prj\Documents>
PS C:\Prj\Documents> dir | rename-item -newname { $_.name -replace '新規 Microsoft Word ','新規案件' }  …Rename-Itemによるファイル名の変更
PS C:\Prj\Documents>
PS C:\Prj\Documents> dir -name  …結果の確認
新規案件文書 1.docx  …正しく変換されている
新規案件文書 2.docx
新規案件文書 3.docx
新規案件文書 4.docx
新規案件文書 5.docx
PS C:\Prj\Documents>



 ファイルの拡張子を変更したり、ファイル名の途中を変更するには次のようにする。

PS C:\Photo> dir -name  …元のファイル名の確認
Photo 2014-11-03-003.jpg  …このファイル名の末尾には年月日と連番が付いている
Photo 2014-11-03-005.jpg
Photo 2014-11-03-011.jpg
Photo 2014-11-03-013.jpg
PS C:\Photo> dir | rename-item -newname { $_.name -replace '\.jpg','.jpeg' }  ….jpgを.jpegに変更してみる
PS C:\Photo> dir -name
Photo 2014-11-03-003.jpeg  …拡張子が変更された
Photo 2014-11-03-005.jpeg
Photo 2014-11-03-011.jpeg
Photo 2014-11-03-013.jpeg
PS C:\Photo> dir | rename-item -newname { $_.name -replace 'Photo 2014-','2014秋 旅行写真' }  …ファイル名の先頭から変更してみる
PS C:\Prj\Photo> dir -name
2014秋 旅行写真11-03-003.jpeg  …ファイル名の先頭が正しく変更された
2014秋 旅行写真11-03-005.jpeg
2014秋 旅行写真11-03-011.jpeg
2014秋 旅行写真11-03-013.jpeg
PS C:\Prj\Photo> dir | rename-item -newname { $_.name -replace '旅行写真([0-9]+)-([0-9]+)-([0-9]+)','京都 $1月$2日$3' }  …ファイル名の途中を変更してみる
PS C:\Photo> dir -name
2014秋 京都 11月03日003.jpeg  …ファイル名の途中が正しく置換された
2014秋 京都 11月03日005.jpeg
2014秋 京都 11月03日011.jpeg
2014秋 京都 11月03日013.jpeg
PS C:\Photo>



 最後の例では、特定の文字を正規表現で抽出して置換している。旧文字列にある「[0-9]+」は1桁以上の数字列を表す正規表現だ。また「( )」で囲んだ部分を、新文字列中では「$1」「$2」「$3」として参照している。

 2カ所以上変更したければ、Rename-Itemコマンドを繰り返すか、「-replace 」パラメーターの部分をさらに追加して記述すればよい。

●名前変更のシミュレーション

 実際にファイル名を変更する前に、どのような名前に変更されるのか、変換指定が正しいのかなどを確認したければ、Rename-Itemに「-WhatIf」パラメーターを付けてシミュレーションさせるとよい。変更前と変更後のファイル名の両方が表示されるが実際の変更は行われないので、変更内容を事前に確認できる。具体的には「Rename-Item」の直後に「-WhatIf」というパラメーターを追加する。表示結果が正しければ、[↑]キーで直前のコマンドを呼び出し、[←]や[→][Del]キーなどでコマンドの内容を再編集してから[Enter]キーを押して実行させる。

 また「-Confirm」パラメーターを追加すると、名前を変更するかどうかを1ファイルごとに問い合わせるようになるので、不適切な名前変更ならスキップできるようになる。

「Tech TIPS」のインデックス

Tech TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。