2019年11月20日

バッチファイルでファイル名に連続した数字をつける


バッチファイルを使ってファイル名に連番をつける方法をメモメモ…



たとえばテキストファイルが複数あるときに、それぞれのファイル名を MyText1.txt、MyText2.txt、MyText3.txt … のような連続した数字でリネームしたい場合の対処法です。もちろんファイル名だけではなくフォルダ名にも適用できます。


バッチファイルの作成方法


バッチファイルの作り方は簡単です。テキストドキュメントを新規作成し、適当なエディタ(メモ帳など)を使ってコマンドを書き、拡張子を .bat に変更して保存しなおすだけです。詳しい手順は前回の投稿で書いたので必要であれば参考にしてください。


コマンド


バッチファイルの内容はこんな感じです。

setlocal enabledelayedexpansion

set prefix=MyFile
set num=0

for %%F in (%*) do (
  set extention=%%~xF
  ren %%F !prefix!!num!!extention!
  set /a num+=1
)

endlocal


ファイルやフォルダをバッチファイル(.bat)上にドラッグ&ドロップすると、自動的に MyFile0、MyFile1、MyFile2 …とリネームされるはずです。"MyFile" の部分を変更したい場合はset prefix=のうしろを好きな文字列に書き換えてください(スペースを含む場合はダブルクォーテーション " " でくくります)。開始番号を 0 からではなく別の数字にしたい場合はset num=のあとの数字を変更すればOKです。

ポイントとしてはやはり1行目のsetlocal enabledelayedexpansionでしょうか。バッチファイルではループ処理などでの変数を読み込むタイミングが他の言語とは異なります。そのため、7~9行目のループ内の処理がきちんと順番に実行されるように遅延環境変数の展開を有効化する必要があるというわけです。遅延環境変数については次のページの解説がわかりやすかったです。

バッチファイル界の魔境『遅延環境変数』に挑む(おまけもあるよ) - Qiita


注意点としては、多数のファイルを一度にD&Dした場合に、コマンドの文字数制限にひっかかってしまいうまく実行できない場合があるということです。そういう場合はディレクトリから直接ファイルを探して取得するようにしたほうがいいかもしれません。たとえば6行目の(%*)(*.txt)と変更するとD&Dしたファイルではなく同一ディレクトリ内のすべてのテキストファイルを対象にできます。

それから、対象のファイルに特殊な文字列が含まれていた場合、そのファイル名自体がコマンドとして読み込まれてしまいエラーになることがあるそうです(参考ページ)。滅多に起きないとは思いますが一応書き留めておきます。


コマンド (ゼロ埋め)


ついでと言ってはなんですが、指定した桁数になるようにゼロ埋めする場合のコードも書いてみました。

setlocal enabledelayedexpansion

set prefix=MyFile
set pad=3
set num=0

for %%F in (%*) do (
  set padnum=00000000!num!
  set extention=%%~xF
  ren %%F !prefix!!padnum:~-%pad%!!extention!
  set /a num+=1
)

endlocal


数字の前にいくつかゼロをくっつけてから指定した桁数で切り取るという処理を行っています。桁数は4行目のset pad=で指定可能です。MyFile1 は MyFile001、MyFile10 は MyFille010 というように桁を揃えることができます。

おまけ ゼロ埋め&ファイル名の重複を避ける

コマンド (ゼロ埋め&ファイル名の重複を避ける)


コメント欄でリクエストがあったので「同一ディレクトリにすでに同じ名前のファイルがあった場合は自動的に別の数字をつける」という機能も実装してみました。使い方はこれまで同様バッチファイル上にD&Dするだけです。

setlocal enabledelayedexpansion
 
set prefix=MyFile
set tempprefix=temp_
set pad=3
set num=0

for %%F in (%*) do (
  ren %%F "!tempprefix!%%~nxF"
)
for %%F in (%*) do (
  call :rename "%%~dpF!tempprefix!%%~nxF"
)
exit /b

:rename
  set file=%1
  set path=%~dp1
  set extention=%~x1
  :again
  set padnum=00000000!num!
  set newname=!prefix!!padnum:~-%pad%!!extention!

  if exist !path!!newname! (
    set /a num+=1
    goto again
  )

  ren !file! !newname!
  set /a num+=1
exit /b

endlocal


たとえば MyFile000 MyFile001 MyFile002 NewFile1 NewFile2 という5つのファイルが同じフォルダの中にあった場合、数字が重複しないように MyFile000 MyFile001 MyFile002 MyFile003 MyFile004 というふうにリネームします。別のフォルダに入ったファイルや拡張子の違うファイルだとうまく機能しないので注意。

技術的なポイントとしては
  • もしも名前変更したいファイルの中にすでに名前変更後のものが含まれていた場合ややこしいことになるので、一旦ファイル名の先頭に適当な文字列をくっつけて別の名前にしておく(MyFile000 → temp_MyFile000)。文字列は既存のファイル名と重ならないようにset tempprefix=で指定。
  • ファイル名が存在するかどうかのチェックは24行目のif existで行う。重複していればgotoで20行目まで戻って重複しなくなるまで数字を増やす。
  • バッチファイルでは、forループ構文の中でgotoを使うとその時点でカウント変数%%Fが破棄されループも解除される仕様。実際の処理はcallを使ってサブルーチンとして呼び出す。

正直バッチファイルだけでここまで複雑な処理をしたことがないので、これで正解なのかはわかりません(もっとシンプルな方法があるかも…)。もしかしたら不具合が出る可能性もあるので確認しながら使ってください。


おまけ 2 フォルダ内のファイルをシャッフル

コマンド (フォルダ内のファイルをシャッフル)


コメント欄でリクエストがあった「フォルダ内のファイルをシャッフル(ランダムに並び替え)してその上で連番にリネームする」という処理です。好きなフォルダ(複数指定可)をバッチファイル上にD&Dすると、その中のすべてのファイルがリネームされます。

set prefix=MyFile
set startNum=0
set pad=3

for %%A in (%*) do (
  cd %%~fA
  setlocal enabledelayedexpansion

  @rem フォルダ内のすべてのファイルをランダムな数値でリネームする

  for %%B in (*) do (
    set extention=%%~xB
    ren "%%B" !random!!extention!
  )

  @rem フォルダ内のすべてのファイルを連番でリネームする

  set num=!startNum!
  for %%B in (*) do (
    set padnum=00000000!num!
    set extention=%%~xB
    ren "%%B" !prefix!!padnum:~-%pad%!!extention!
    set /a num+=1
  )

  endlocal
)

13行目では組み込み変数randomを使って0~32767の範囲の整数値を取得しています。なお、システム環境によってはループ処理が名前順に実行されず、うまく動作しない場合があるかもしれません。そのときは dir コマンドの /o:n オプションを利用するなどさらにひと手間必要になります。


9 件のコメント:

  1. はじめまして! 近江と申します。
    記事を参考させていただきました。パソコン歴はもう40年位になりますが、DOSコマンドは遠の昔に忘れてしまい、大変助かりました。大昔は640MBの壁をいかに超えるかなどEMSをいじって楽しんでましたが今では齢60歳となり昔の記憶が段々と薄れてきています。WindowsになってMS-DOSのコマンド(正式にはパワーシェルでしょうが・・・)の基本を経験して良かったと感じています。また、何かの折には拝見させていただきます。
    この度は、ありがとうございました。
    https://asamai-cts.jp  近江

    返信削除
  2. こんばんは
    ひとつわからない事があるのですが
    5つのファイルがあり
    名前01
    名前02
    名前03
    名前04
    名前05
    ってなったんですがたとえば後からもう1つファイル増えた場合で後から増えたファイルにパッチ当てると名前01になると思うのですが、これを同フォルダ内に入れて増えたファイルだけパッチあてて同名前がある場合(今回の場合名前01~05が既にあるため)自動で名前06ってなるようにできたりしますか?
    説明下手ですみません
    やっぱり後から増えたファイル含めて全部まとめてパッチ当てなおすしかないですか?
    全部当てなおすと名前01~名前06になりますがほんの少しだけ手間で....



    返信削除
    返信
    1. 記事の最後に追記しました!
      これを使って新しいファイルにバッチ処理をあてると、同じフォルダの中にある他のファイルと重複しないようなファイル名がつけられる…はず…です。もしうまく動作しなかったらスミマセン…

      削除
    2. ありがとうございます。思ってたやつです。
      こんなに早く対応してもらえると思ってませんでした。
      ホントに助かります。
      ブラウザ上で動くピアノ(いつもはスマホでやってるけど使いずらいので)とか他にも色々参考にしてるので
      今後もブログ拝見してるので頑張ってください
      ありがとうございました。

      削除
  3. はじめまして!
    CornPOPPA様、痒い所に手が届くバッチファイルを公開してくださりありがとうございます。 現在私は、フォルダ内に保存された音楽や画像などを毎回ファイルをシャッフルして連番としてリネームして、スライドショー的な操作をしたいと考えており調べてましたら、こちらのサイトに辿り着きました。

    CornPOPPA様のバッチ処理内に「毎時フォルダ内のファイルをシャッフル(shuf)して連番リネームする処理(フォルダをD&Dしてフォルダ内のファイルを処理)などは可能でしょうか?

    プログラム的なことはド素人で、毎回ネット内を調べ、公開してくださっている方のを使わせて頂いております。


    下記は、英語圏を含め探していたところですが、すべてファイル名をランダムに書き換えてしまうものばかりで思ったものが見つからずです。

    How to shuffle file names randomly in a directory
    https://unix.stackexchange.com/questions/506302/how-to-shuffle-file-names-randomly-in-a-directory

    Randomly Shuffle File Names in a Folder
    https://www.reddit.com/r/Batch/comments/gt4u27/randomly_shuffle_file_names_in_a_folder/

    How can I arrange all the image files in an folder randomly?
    https://stackoverflow.com/questions/52457832/how-can-i-arrange-all-the-image-files-in-an-folder-randomly


    バッチファイル : ファイル順のシャッフル
    https://logicalerror.seesaa.net/article/128449047.html

    こちらもファイル名をランダムで書き換えていますが、毎回シャッフルしてここにCornPOPPA様のバッチ処理が組み込めないかと、見よう見まねでチャレンジしてみましたが、バッチコード、構文が書けないので断念しております。

    もし可能でしたら毎回バッチを実行後に「シャッフル連番リネーム」させることはできないでしょうか? お手数ではございますが、お力をお貸し頂けると幸いです。

    返信削除
    返信
    1. 記事の最後に追記しました!
      フォルダを指定すると、その中のファイルをシャッフルし、さらに連番でリネームします。

      削除
    2. CornPOPPA様、お礼の返事が大変遅くなりました。 作ってくださりありがとう御座います。 早速試してみて、ちゃんと入れ替わっているので、何度も繰り返して眺めながら凄ーい!と感動してます。 これで毎回、雰囲気も気持ちも変えられ、楽しめる環境が得られました。 この度は、本当にありがとうございました。

      削除
  4. はじめまして。
    ファイル名に連番をつけるという処理が必要になり手作業((+_+))、、、と途方に
    くれていたところ、こちらの記事に辿りつき活用させていただいております。
    ほんとにありがとうございます。
    その中で一つ質問があるのですが、既にデータファイルに名前を付けている
    ファイル名に連番の数字だけ追記したいのですが可能でしょうか。
    自分でも試行錯誤し、
    3行目の set prefix=MyFileを消したりしたのですが
    全くうまくいかなくて、、、、( ;∀;)
    その都度set prefix=MyFileのMyFileを変更すればいいのですが、
    ファイル名の最初に記号(●など)を表記することがあり
    その際、文字化けするので起動バッチを使用する前に
    自分でファイル名は変更し後で連番を付けたいと考えてります。
    お忙しいところ恐縮ですが、ご存知であれば教えていただきたいです。
    よろしくお願いいたします。

    返信削除
    返信
    1. 既存のファイル名に番号を追記したい場合は
      ren %%F !prefix!!num!!extention!
      の部分を
      ren %%F %%~nF!num!!extention!
      と書き換えてみてください。%%~nFは「拡張子を除くもとのファイル名」という意味です。これで「ABC.txt」という名前のファイルが「ABC1.txt」のようにリネームされます。基本的にはこの行を変更すれば好きなファイル名をつけることが可能です。たとえば
      ren %%F %%~nF_!num!!extention!
      とすれば「ABC_1.txt」のようにもとのファイル名と番号の間にアンダースコア(_)を追加できます。

      削除