【BATCH】フォルダをドラッグ&ドロップすると中身のファイルをコピーするバッチファイル

とある面倒くさい作業があって、それを救済すべくバッチファイルを用意しました。

その作業というのは、介護保険でケアプランデータ連携システムという、書類手続きをインターネット経由でデータでやり取りできるように施策がスタートしまして、介護業務システムから専用フォーマットのデータを出力する作業があります。

出力したデータをケアプランデータ連携システムにアップロードするのですが、介護業務システムの仕様で相手の事業所ごとにフォルダが作成されてその中にデータが入りますので、アップロードするときに一つひとつフォルダを開かないといけません。これがかなり面倒くさそう。

そういうわけで出力されたデータを一つのフォルダに全部まとめてしまうバッチファイルがあれば楽だろうということで作りました。

プログラムの要件

まず実行方法ですが、対象となるフォルダは作業の度に作成されるので選択できる必要があります。

そこでユーザーが対象フォルダを選んで、それをbatファイルにドラッグ&ドロップすることで実行できるようにしておきます。

入出力フォルダ内には複数のフォルダが存在します。このフォルダすべてに必要なデータが入っています。

一つフォルダを開いてみると、さらにその中に2つフォルダが入っています。

この内、「export」というフォルダの中身だけが必要で、「import」というフォルダの中身については不要です。というか混ざると厄介です。

「export」フォルダの中にはCSVデータが複数入っていて、すべてのデータが必要です。ただし、CSV以外のデータについては不要です。

Aフォルダ、Bフォルダ、CフォルダすべてのフォルダにこのCSVデータが存在しています。開いた先でさらにexportフォルダを開く必要もありますので、フォルダを一つひとつ開いてアップロードという作業がいかに面倒くさいかは想像に容易いです。

コピーして一つのフォルダにまとめてしまえば、アップロードはデータを全選択すれば済みます。

バッチファイルの作成

という要件を満たしたバッチファイルのコードがこちら。

@echo off

for %%f in (%*) do (
    set parentDir=%%f
)

if %parentDir%.==.  (
    exit
) else (
    pushd %parentDir%
)

set time2=%time: =0%
set outputDir=C:\Users\hoge\Desktop\%date:~0,4%%date:~5,2%%date:~8,2%%time2:~0,2%%time2:~3,2%%time2:~6,2%\
md %outputDir%

for /d /r %%a in (*export) do (
    pushd %%a
    for /r %%b in (*.csv) do (
        copy %%b %outputDir%%%~nxb
    )
)

上記のキャプチャではgetFoldersFile.batというファイル名を付けてますが適当でいいです。「ここにフォルダを入れろ.bat」とかがユーザーにやさしいかもですね。

フォルダを選択しないと実行しないようにする処理

フォルダをドラッグ&ドロップしたら、そのフォルダに対して処理を行うようにしていますが、ダブルクリックでも実行できてしまいます。

その場合はバッチファイルを置いているフォルダ内で処理が行われてしまうため、意図しないデータをコピーしてしまう可能性があります。

コピーなら取り返しがつきますが、応用して移動とか削除するようなプログラムを作る場合は避けるように設計した方がいいでしょう。

4行目のparentDirにはドラッグ&ドロップで入れたフォルダのファイルパスが格納されますが、もしダブルクリックで実行すると値はnullとなります。

7行目以降にnullであれば中止するという記述をしているのですが、nullのときの判定に少し手こずりました。どうやらifの条件にnull値の変数を使用するとエラーになるみたいです。

そこで適当に「.」をくっつけて、比較対象も同様に「.」を付けておけばnullになるのを避けることができます。これ考えた人頭いいなぁ。

僕は下記のリンクを参考にしました。

【CMDバッチ】変数がNULLかどうかを調べる | ア ル ゴ リ ズ ム 日 和
コマンドプロンプトでは文字列の表現にクォーテーションを使用しないので、 空文字の表現にはひと手間加えます。 たとえば環境変数nyaの中身が空文字で...

フォルダが選択できていればpushdを使ってそのフォルダに移動します。

コピー先のフォルダを準備する

先にコピー先のフォルダを作成しておきます。コピーする際にフォルダが存在していないとエラーになります。

フォルダ名は何でもいいのですが、1日に複数回実行する可能性を考慮して、日付と時刻でフォルダ名を付けるようにします。

%dateで日付データ(yyyy/mm/dd)、%timeで時刻データ(hh:mm:ss)が取得でき、区切りの/や:を取ってやります。これらの文字列が混ざるとフォルダ名として不適切です。

14行目の「%date:~0,4」という記述は、時刻データの0文字目から開始して4文字を取得するという意味です。「2023/06/23」なら「2023」が取れます。これを月と日も同様に行います。

時刻については、もし0時55分2秒の場合「 0:55: 2」と十の位の0は空白となってしまいます。フォルダ名に空白が混ざると支障がありそうなので、13行目で先に空白を0に置換しておいた変数「time2」を用意してから、日付と同様に区切りの文字列を取り除く処理をしています。

変数「outputDir」に任意のパスと、日付と時刻から成る14桁の文字列を入れて、mdでフォルダを作成します。

こんな感じでフォルダができあがります。

Windowsのバッチファイル中で日付をファイル名に使用する
バッチファイルの中で日付や時間をベースにしたファイル名を利用したい場合がある。このような用途では、%date%や%time%変数を利用して、ファイル名を合成すればよい。ただし、ファイル名に使えない文字は取り除きつつ、日付と時刻の情報を抜き出...

必要なデータだけをコピーする

今回の要件は指定フォルダ内の全フォルダ内にある特定のフォルダ内の特定のデータという、ややこしい条件のため少し複雑でした。

これがもしフォルダ関係なくすべてのCSVデータとかだったらもっと簡単でした。

流れとしては、

  1. exportフォルダだけをピックアップして、各exportフォルダに移動
  2. 1で移動した先のフォルダ内にあるCSVデータだけを指定フォルダにコピー

というのをexportフォルダの数と、その中のCSVデータの数だけループするというものです。

for文の後ろの値について、/dはフォルダ(ディレクトリ)を対象にする、/rはフォルダ内にある子フォルダを対象にするものです。

カッコ内の引数は対象となるファイル名の条件を入れます。「*export」「*.csv」とワイルドカードを使って記述します。

copyは元のファイルパス、コピー先のファイルパスの順番に記述します。このコードでは変数「%b」がコピー対象のデータなのですが、ファイルパスもくっついてるのでこれが不要。

そこで「%~nxb」と記述します。nはファイル名、xは拡張子、末尾のbは対象の変数名を意味し、ファイルパスを取り除きます。あとはコピー先のファイルパスをくっつければ無事コピーできます。

こんな感じで必要なCSVデータだけ取り出すことができました。よかったですね。

FOR /R = FOR文でサブフォルダーも処理対象にする - Windowsのコマンドプロンプト(bat,cmd)
WindowsコマンドのFOR < ▲ > ○Windows XP FOR文を普通に書くと、一つのフォルダーの中だけでファイルを探します。 /R オプションを付けると、サブフォルダーの中にあるファイルも処理の対象にする事が出来ます。 o ワ...
バッチでファイルパスやファイル名を取得する方法 – Rainbow Engine

コメント

コメントする前にお読みください

プログラミングに関する質問について、詳細なコードはお答えしませんのでご了承ください。
また、迷惑コメント防止のために初回のコメント投稿は承認制です。投稿が反映されるまで少し時間がかかります。