fc2ブログ

とまと あんらいぷ…

エンジニアの活動記録とかつぶやきとか

GitHub
スポンサードリンク

バッチファイルでファイル名に連番を付けてコピーする~実行解説

同じファイルに0埋めされた連番を付けてコピーしたい。


テストデータ作成しかり、月次のファイルの作成しかり。

そんなシチュエーションに遭遇した場合
手作業で頑張ってファイルを作るか
ご希望の機能を持つファイラやツールを探すかしないといけない。

ところが昔からあるWindowsの命令言語にはコマンドプロンプトというものがあって、
ものすごく標準装備なのに周りは誰も使ってない。
ちょっとぐらい使いこなせてもいいんじゃないでしょうか。

ということで使いこなせていないコマンドプロンプトを少し勉強しておこうと思い
メモしておきます。

今回やりたいことは以下のとおり

1.複製したいファイルを用意する。
2.用意したファイルに対して、希望した枚数を指定したファイル名に変更し、連番を付加してコピーする。

というわけで、できあがったbatファイルは次の通り。



実行結果は
batファイルと同じフォルダに存在するtxtファイルに
連番を付加してコピーしまくります。

実行後のフォルダ内はこんな感じ。

コピー実行

コピー元のtxtファイルが複数あれば、その分倍増するので
実行には注意して下さい。

バッチファイルの解説


さて、このコマンドを作成するのに1時間もかかってしまいました。
昔からある言語なので構文が直感的じゃないんですね。

%1やら%%fやら、よく分からない文法がでてくるので
まとめておきます。
上から見て行きましょう。

echo off


これは、バッチファイルを実行した時とか
コメントを非表示にする命令です。

個人でバッチファイル作成を行なって
コマンド実行中の画面表示どどうしても見たくない
とかいう事がなければ
特にあっても無くてもどうでもいいです。

echoについて詳しく説明されているサイト様はこちらです。
コマンドプロンプトを使ってみよう!

set


set は、変数に値を代入します。
set n=0こんな感じ。

変数である「n」に数値の0を代入しています。
nは自由に名付ける事ができます。

同じsetですが、11行目あたりに
set /a n=n+1こんなのもあります。

/a ってなんだ!?
と思うんですが
/aを付けると、四則演算の結果を代入できるようです。
四則演算を行うには、いちいち/aオプションを付けないとダメなので注意しましょう。

ちなみに、setの後の「=」の前後にスペースを入れると、
スペースも値の一部として認識されるらしいのでこちらも注意が必要です。

他にも/pを付けるとか色々ありますので興味のある方は以下のサイトを参照下さい。
Windows環境変数

for


forは、殆ど全てのプログラム言語に存在する
「ループ処理」というやつです。

バッチファイルのfor文も、ちょっと使おうと思っても
慣れないうちはなかなか動かなかったり、
思い通りに作れなかったりします。

それもそのはず、
バッチファイルを作成する時と
そのままコマンドラインで使うときで構文が微妙に違うし
オプションを付加する時もこれまた微妙に違います。

コマンドプロンプトで、「for /?」と打ち込むと
説明が出てきますが、すごく不親切です。
というか、「for」自体の説明だけなので
これを組み合わせてお目当ての動きを作るというところまでは
説明されていません。

解りやすくまとめてくれてるサイト様はやっぱりこちらです。
コマンドプロンプトを使ってみよう!

今回作成したfor文はバッチファイルなので

構文は以下のとおりです。
■ for %%変数名 in (セット) do コマンド名 [コマンドパラメーター]

  for %%f in ( *.txt ) do call :copyFile %%f
日本語で
「変数名」
「セット」
「コマンド名」
「コマンドパラメーター」

となっているところが自分で書き換えるところです。

■変数名

「変数名」は%%fとしてます。
そういうものだと思いましょう(笑)
この%%fに、(セット)で指定した「複数の何か」が入ってきます。

■セット

「複数の何か」を指定するために
「 *.txt」と書いています。

「*」は、Windowsではワイルドカードと言って
「なんでもいい何か」です。

最初に作成したバッチファイルと同じフォルダ内に
「元ファイル.txt」を用意しましたが、
この「元ファイル」という部分が該当します。

「.txt」はきちんと指定しているので
「.txt」ファイル全てが一致します。

ちなみに「*.*」と書くと
テキストファイルだけでなく全てのファイルがマッチします。

もちろん、バッチファイル自身も対象となるので
「*.*」で実行すると結果は・・・

ミスった時

色々と気をつけましょう。

■コマンド名・コマンドパラメーター

コマンド名??
と意味の分からない説明をつきつけられて
ああなるほど って思えたら素敵ですね。
実際は全く分かりません。

例えばコマンドプロンプトのコマンドには
echo というのがあって、echo の後ろに書いた文字を
そのまま画面に出すコマンドがあるので打ち込んでみましょう。

echo

はい。
一回目間違ってますが(笑)
とにかく、1,2,3それぞれにechoが実行されるわけですね。
echoの後ろに%nって書いてますが、forの後ろにも%nって書いてるので
セットに書いている「1 2 3」が順番に入って来るたびに
%nの中身が入れ替わって、その都度echoが実行されてるわけです。

セットには
「*.txt」と書いたり「1 2 3」と書いたり・・・
色々できるのでこれもまたコマンドプロンプトのヘルプが不親切な原因ですね。

コマンド名の話に戻りましょう。

ラベル


今回はコマンド名のところに
:copyFile %%f
と書きました。

:copyFileってなんだ?

「:何か」と書くと、バッチファイルでは「ラベル」というものを作ることができます。
言い直すと「:ラベル名」です。

ラベル名は自分で好き勝手つけてOKです。

このラベル、何の役に立つかというと
他のコマンドプロンプトの命令と組み合わせて使います。
まぁ・・・「目印」とでも認識して下さい。

基本的なプログラム言語は通常
書いてある命令を上から順番に処理するのがルールです。

そのルールに従うと、for文の「コマンド名」を書くと、そのコマンドがが実行されるべき所ですが
ラベル名を書くことで、そのラベル名にジャンプします。

つまり8行目の

:copyFile

if "%n%"=="10" (goto :finish)
set /a n=n+1
set /a exp=1000+n
copy %1 %~n1%exp:~1%%~x1

goto :copyFile

この最初の:copyFileに飛んで、
そこの位置から、下に書いてある命令を実行していきます。

途中、いろいろと書いてますが
最後の「goto :copyFile」の所で
また再び:copyFileラベルにジャンプします。

「goto :ラベル名」は、「ラベルに飛べ」
という命令です。

さて、コマンドプロンプトは上から順番に実行するのがルールです。
内部的には

1行目実行しろ
2行目実行しろ
3行目実行しろ
4行目実行しろ
・・・・

って命令が飛び交ってるわけですね。

それを、「goto :copyFile」なんて書いてます。
上にある「ラベルに飛べ」って書いてるわけです。

1行目実行しろ
2行目実行しろ
3行目実行しろ
4行目実行しろ→1行目に飛べ
・・・・

お気づきの方もいるかもしれませんが
ずーっとクルクルクルクルします。
プログラミングの世界では、これを「無限ループ」と読んでいて
パソコンがフリーズしたり、「バグ」と呼ばれたりする
あんちくしょうな奴です。

バッチファイルでgoto文を書いて、処理が帰ってこなくなったら
まずは無限ループを疑いましょう。

if



「無限ループ」は一度走り出したら処理が終了しないじゃじゃ馬ですが
それを制御してくれるのが「if」です。

ifもforと同じで、いろんな書き方があります。
またまたコマンドプロンプトで「if /?」と入力して使い方を調べてみると
3つぐらい出て来ました。

if文

今回は2番目の
IF [NOT] 文字列1==文字列2 コマンド
を使います。ちなみに、[]で括られた部分である[NOT]は
「あってもなくてもいいよ」という意味です。

これはWindowsのお決まりの記述方法で
これを知らなければヘルプさえ読むことができないという状態になるので
覚えておきましょう。

さて、くるくる無限ループ中の一番最初に
if "%n%"=="10" (goto :finish)
って書いてますね。
これは、nが10だったら("%n%"=="10")、
:finishというラベルにジャンプしろ((goto :finish))っていう命令です。

この司令により、くるくる無限ループを抜けることができます。
その制御を行ってるのが"%n%"なので、こいつは非常に大事ですね。

くるくる中のifのすぐ下に、
set /a n=n+1 って書いてますね。
これはnにn+1を足しなさいという命令で
くるくるが1回行われる度にnが1増えます。

こうすることで、nが10になった時にくるくるを終わることができます。

ifやforを利用する時の注意点も参照下さい。
バッチファイルのif文やfor文で気をつけること

変数の展開



最初に「set n=0」と書いて
nに0をセットしました。
これは「数値」です。
同じく10も「数値」です。

ifの使い方は

IF [NOT] 文字列1==文字列2 コマンド

なので、nは「文字列」として扱わなければいけません。

そこで「"」(ダブルクオーテーション)で括っている中の部分は文字列とする
というこれまたあまり記載のないルールに従って
文字列にしてしまいます。
10も忘れずに"10"にします。

なんでnは"n"じゃないの?

と最初の疑問がでてきますね。
"n"と書くとどうなるか?

やっぱり、くるくる無限ループになってしまって処理が戻って来ません。

"n"と書くと
本当にn文字列になってしまうので「nという文字列が10という文字列と一緒だったら」
という比較になってしまいます。絶対一緒にはなりません。

nは数値の変数であって、"n"という文字ではありません。
でも"n"と書いてしまうとアルファベットの"n"になってしまう。

でも数値の変数である変数nの中身を文字列として展開したいよ!
そんな時に%n%を書くのです。

%n%と書いておけば、くるくる中のif文で変数が展開されて
"0"=="10"
"1"=="10"
"2"=="10"
"3"=="10"
・・・・
という比較を行なってくれます。

次はコピー処理の大本命copyコマンドを見て行きましょう。

copy


コピーコマンド

これがcopyのヘルプです。
[]ばっかりでイヤになりますね。

これをかいつまむと、
copy コピー元ファイル コピー先ファイル という書き方をしろということです。
「copy file1.txt file2.txt」
って書けば、「file1.txt」を「file2.txt」にコピーします。

日々のパソコン操作に置き換えると、
右クリックでコピーして、貼り付けして、名前をfile2.txtに換えるのと同じです。

多分こっちのほうが早い・・・

でも今回は大量のファイルをコピーするのが目的なので
頑張ってくるくるループ中にcopyコマンドを書きましょう。

copy %1 %~n1%exp:~1%%~x1

あーもういやだ。
%1とか、%~n1とか意味の分からないものばっかり出て来ましたね。

混乱しないようにまとめていきます。

■%1とは
まず、%1ってなんだよ。
です。これは先ほどの「コマンドパラメーター」に深く関連します。
forで、%%fを「コマンドパラメーター」に指定しましたよね。

「コマンド名」の中では、「コマンドパラメーター」が%1に変わります。
コマンドパラメーターを複数指定した時は
%1,%2,%3・・・と増えていきます。

試しにこんなバッチを作ってみました。
ファイル名は「echo.bat」としました。


で、コマンドラインで「echo.bat」の後ろに「あ い う」をくっつけて実行!
すると

エコーコマンド

コマンドラインパラメーター展開するよ!
あ い う

と表示されます。
%1と%2と%3が、渡されたコマンドラインパラメーターに置き換えられました。

話を戻します。

今回は、%%fは1つしか渡していないので%1だけです。
copyコマンドの一番最初に%1があるので
forのセットで取得した最初のtxtファイル
つまり「元ファイル.txt」が展開されます。

次にコピー先ファイルの指定部分を見ましょう。

残りの部分はこんなの。一つづつ分解していきます。

「%~n1%exp:~1%%~x1」

まず
n1とx1を見て行きましょう。
これは、%1の友達です。

「%~」という記述は、コマンドパラメーターが関連してると思って下さい。

つまり
「%~n1」で1つ
「%~x1」で1つ
という意味です。

%~n1とは何か?

これ、正式なヘルプどこにも見つけられないんですよね・・・
知ってる方います?

綺麗にまとめてるサイトさんから引用すると。
使い方は以下のとおり
修飾子機能・用途
%~1全ての引用句(")を削除して%1を展開する。
%~f1%1を完全修飾パス名に展開する。
%~d1%1をドライブ名だけに展開する。
%~p1%1をパスだけに展開する。
%~n1%1をファイル名だけに展開する。
%~x1%1をファイル拡張子だけに展開する。
%~s1展開されたパスはMS-DOSの「8.3形式」でも短い名前だけを含む。
%~a1%1をファイル属性に展開する。
%~t1%1をファイルの日付・時刻に展開する。
%~z1%1をファイルのサイズに展開する。
%~$PATH:1PATH環境に指定されているディレクトリを検索し、最初に見つかった完全修飾名に%1を展開する。環境変数名が定義されていない場合、また検索してもファイルが見つからなかった場合は、この修飾子を指定すると空の文字列に展開する。
コマンドプロンプトを使ってみよう!


つまり
%~n1で「%1をファイル名だけに展開する。」
%~x1で「%1をファイル拡張子だけに展開する。」
です。

%1は、「元ファイル.txt」なので、それぞれ
「元ファイル」「.txt」です。

「%~n1%exp:~1%%~x1」を展開して、残りの意味不明な部分を記述すると。

「元ファイル%exp:~1%.txt」

ですね。

残りの「%exp:~1%」部分を見て行きましょう。

expは、copy命令のすぐ上で
set /a exp=1000+n
としてnに1000を足した値が入っています。

1回めのcopy命令の時は1001が入ってます。

今回は、元ファイル001.txtとした連番を作成したいので
なんとなく1001の後ろの3桁をファイル名にしたいのだなと予測いただければ嬉しいです。

上の方で「変数の展開」方法を少し記載しましたが
変数の変改方法もいろんなことができます。

これも、こちらを引用すると・・・
BATファイルで文字列の切り出し

書式意味
%V%変数Vの値全体
%V:~m%m文字目から、最後まで
%V:~m,n%m文字目から、n文字分
%V:~m,-n%m文字目から、最後のn文字分を除いたもの
%V:~-m%後ろからm文字目から、最後まで
%V:~-m,n%後ろからm文字目から、n文字分
%V:~-m,-n%後ろからm文字目から、最後のn文字分を除いたもの
%V:c1=c2%文字c1を文字c2に置換する。それぞれ複数の文字を指定することも可能

というわけで、
「%exp:~1%」
は、1001の1文字目から最後までという意味になり、
"001"が展開されます。
変数やコマンドパラメーターが全て展開されると
「元ファイル001.txt」となります。

ところで、1001の1文字目から最後までだったら全部では?
という疑問は正しいです。一般的な感覚です。

ですが、プログラミングの世界では、1つ目を0と考える事が多いです。
ですから、1001の千の位は0文字目となり、001を取得したい場合は1文字目からとなります。
中には1つ目を1文字目を表記する言語があるので、これまた厄介なのですが・・・

%1や%変数名%で括る記述が混在してるので、すんなり読み解くのは時間がかかりますね。
コマンドパラメーターの展開と、変数の展開は
%1とした記述と、%変数名%で括る方法とで違うので注意が必要です。

さて、どんなファイル名に展開されるかが理解できて、
お目当ての個数分ファイルをコピーできれば
あとは、プログラムを正しく終わらせるだけです。

後片付け exit


バッチファイルの下の方に
:finish ラベルを作成していますね。

このラベルは、くるくるループ条件のifで、nが10になったらジャンプしてくる場所です。
ここで、nを0に戻してますね。
この理由は、セットで取得したファイルが複数ある場合、
再び連番を001に戻すためです。

そして
goto :EOF
と書いてます。
この:EOFは特別なラベルで、「プログラムの最後」を意味します。
昔の名残なので、今は無くても動きます。(ファイルの一番下まで来ると勝手にgoto :EOFが動く)

exit /b


forの下の行に「exit /b」って記載してますね。
goto :EOFは「プログラムの終了」を意味しますが、
プログラムは、上から順番に動くというのがルールでした。

プログラムが最後に動いていたのはどこでしょうか?
for ですよね。
for文のコマンド名の所で無理やりラベル「:copyFile」にジャンプしていたので
EOFはforのすぐ下に戻ってきます。

ここでプログラムを終了させないと
またすぐ下の:copyFileを実行してしまいます。
この時に%1はどこからもコマンドパラメーターとして渡されていないので
エラーが発生します。

そこで、forのすぐ下にexitと書くことで
プログラムを正常に終了させています。

また、/bは最後までプログラムを実行してから終了させるというオプションなので
今回の場合は書いても書かなくてもいいです。(書いてる部分は全部実行したので。)
/bオプションについては、こちらに詳しい内容が書いてますので興味があれば是非。

Windows 2000 活用講座Windows 2000 コマンドライン徹底活用

終わりに


コマンドプロンプト難しいですね。。。
でもファイルのコピーだけでなく
普段手作業で行なっているいろんな操作をバッチファイルにして自動化させることができます。

例えばIPの設定とか
例えばWindowsのアカウントの操作とか・・・

日々同じ事を繰り返すような作業が発生したら
バッチファイルでできないかを検討してみてはどうでしょうか。
▼この記事を読んだ方は、こんな記事も読んでいます。▼

テーマ:プログラミング - ジャンル:コンピュータ

コメント

初歩的な質問でごめんなさい

同じファイル名が定期的に作成されるのでそれを番号をつけて管理したい場合はどうしたらいいでしょうか?例えば1時間毎にA.txtというファイルが出来るのでそれをB1.txt次の1時間後に出来てくるA.txtにはB2.txtという具合に毎回同じファイル名の物を出力だけ番号を加算していく方法ってありますか?

  • 2015/04/03(金) 08:57:21 |
  • URL |
  • tada #3fIBvpkA
  • [ 編集 ]

書き込みありがとうございます

ご質問内容は、定期的に作成される「A」というファイルを、「BX」に置換するには?ということであれば、ren、もしくはrenameコマンドを使って、Aファイルをリネームすることで可能かと思います。
が、Bにつく最大番号が何桁かが分からなかったので、とりあえず日付と時分秒でリネームするサンプルをいかに書いておきますね。
---サンプル---
@echo off
setlocal
:【日付をファイル名用の文字列にする】
set dt=%date:/=%
:【時間をファイル名用の文字列にする】
set ti=%time:~1,7%
set ti=%ti::=%
:【ファイル名を作成(YYYYMMDDHHmmss形式に)】
set FName=%dt%%ti%
:【リネーム実行 ( A.txtを B_YYYYMMDDHHmmssに)】
rename A.txt B_%FName%.txt
--------------------------
あとはこのバッチを、Windowsなどのタイムスケジューラーに登録して定期的に実行してやれば良いと思いかと思います。
以上ですが、回答になってますでしょうか?・・・

  • 2015/04/06(月) 01:02:37 |
  • URL |
  • Dalmore(管理人) #z9jllgzo
  • [ 編集 ]

早速のお返事ありがとうございます。1時間毎にと書きましたが実際には短時間にいくつものファイルが入ってくるのでそれを連番で整理したかったのです。YYYYMMDDHHmmss形式もやってみたのですが秒数まで重なってしまうのでただの連番で出来ない物かと悩んでいて。純粋に1〜連番つけるのは難しいんでしょうか?度々すいません。

  • 2015/04/07(火) 20:20:02 |
  • URL |
  • tada #G2f.Jh/s
  • [ 編集 ]

tada さん
純粋にただの連番でしたら、for文でファイルを一つづつ処理してやれば可能だと思います。

@echo off
setlocal enabledelayedexpansion

cd E:\work
set fileindex=1
for /f %%i in ('dir /b /a-d') do (
echo %%i を %%~ni_!fileindex!%%~xi にリネームします
ren %%i %%~ni_!fileindex!%%~xi
set /a fileindex=fileindex+1
)
pause

こんな感じ。
ただ、このバッチは一回きりしか有効ではなく、もう一度実行するとせっかくリネームしたファイルに更に連番がつくので、どこかのフィルダに待避する必要がありますね。
また、毎度毎度1からの連番を付けてリネームするので、開始数値をどのように取得するか?もキモになります。例えば別のテキストファイルに書き出して、バッチ実行するときはそれを読み出すとか、dirコマンドで取得したファイルの中の一番最大の数字+1を開始数値とするのか?など。バッチのhow toだけでなく設計が必要となりますので、後は臨機応変によろしくお願いします(笑)

  • 2015/04/08(水) 02:18:30 |
  • URL |
  • Dalmore(管理人) #z9jllgzo
  • [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://dalmore.blog7.fc2.com/tb.php/77-353e5da0
この記事にトラックバックする(FC2ブログユーザー)