ファイルリストから読み込んだ対象ファイル(zip)を収集するバッチの作成

ソフトウェア
OpenIconsによるPixabayからの画像

業務で、処理エラーログに出力されたファイルを収集するバッチを、作る事になりそうなので試作してみました。

バッチ作成の経緯

職場のオフィスファイル等に閲覧権限を付与する作業があり、その際 zipファイルが処理できない為、閲覧権限付与ツールの作業ログから、処理エラーが大量に検出されました。とりあえず zipファイルを一か所に集めて、一括して解凍してから処理する事で対応策がまとまったので、先行してそのようなDOSバッチを作ってみました。

DOSバッチ化したい事

バッチ化したい事をざっくり書き出しました。※現場で動かす機会があり、必要な要件を6~8に追加しました。

  1. ファイルリストを読込み対象ファイルを取得する
    • 閲覧権限付与ツールの作業ログはcsv形式、処理ファイルはフルパスで出力される
    • 作業ログからエラー原因のzipファイルのみ、パス付で抜き出してリスト化する
  2. 取得ファイルを格納する特定フォルダを作成する
  3. 収集するzipファイルの上書きを避けるため、特定フォルダ下に連番で命名したフォルダを作成
  4. 収集するzipファイルは連番で作成したフォルダ下に格納する
  5. zipファイルの「格納先パス/取得先パス/コピー状態」をcsvファイルに出力する
  6. 文字コードが「UTF-8」の場合、処理開始時に適用させる
  7. 処理ファイルのパスに半角スペースが含まれる事を考慮する
  8. 取得した処理ファイル名も記録する

作成したDOSバッチ

以上を踏まえて、以下の様なDOSバッチ(collect_listfiles.bat)を作成しました。※要件6~8に対応する為、赤字箇所を追記・修正しました。

collect_listfiles.bat
@echo off
rem 文字コードがUTF-8の場合有効化
rem chcp 65001
rem 実行されているバッチが置かれているフォルダをカレントディレクトリにする
cd /d %~dp0
rem ローカル変数使用開始
setlocal
rem 変数設定
set list=zip_pathlist.txt
set targetPath=""
set collectdir=.\collect
set collectPath=""
set count=1
set copyST=""
set targetFile=""

rem zipファイルのパスリストが無ければ処理エラー
if not exist %list% (
	echo zipファイルのパスリストがありません。
	goto ERROR
)

rem 出力先フォルダが無ければ作る
if not exist %collectdir% mkdir %collectdir%
rem 以前の出力ファイルが在れば削除する
if exist workfolder.csv del workfolder.csv

rem メッセージ表示せずに3秒待機
timeout /t 3 /nobreak > nul

rem ループ中での変数使用設定
setlocal enabledelayedexpansion

rem for /fオプション 変数名 in (ファイル名) do コマンド
rem /fオプション:ファイルまたはコマンドの実行結果を行単位で変数に読込む
rem --------------------------------------------------------------------
rem zipファイルのパスリストを読み出す
for /f "tokens=* usebackq" %%a in (!list!) do (
  rem zipファイルの取得先パス
  set targetPath="%%a"
  
  rem カウンタを0パディングして3桁連番作成
  set num=00!count!
  set num=!num:~-3,3!
  rem 格納フォルダ下に「expand + XXX(連番)」フォルダ追加
  set collectPath=!collectdir!\expand!num!
  
  rem 「expand + XXX(連番)」フォルダ作成
  if not exist !collectPath! mkdir !collectPath!
  
  rem zipファイルを取得先から格納先にコピー
  copy /y !targetPath! !collectPath!
  
  rem コピーコマンドの成功・失敗を検出する(0以外はエラー)
  if !ERRORLEVEL! equ 0 (
	set copyST="success"
  ) else (
	set copyST="error"
  )

  rem zipファイル取得先のフォルダパスを取得
  set targetPath=%%~dpa
  
  rem 取得したzipファイル名を取得
  set targetFile=%%~nxa
  
  rem zipファイルの「格納先パス,取得先パス,コピーステータス,ファイル名」順で、csvファイル出力
  echo !collectPath!,!targetPath!,!copyST!,!targetFile!>> workfolder.csv
  
  rem カウンタのカウントアップ
  set /a count+=1
)

rem ローカル変数使用終了
endlocal

:ERROR
rem 一時停止「続行するには何かキーを押してください… 」を表示して処理を抜ける
pause
@echo on

コマンド説明

バッチの構成は直近で作成したバッチから流用し、基本事項ついては、初回に作成したバッチに詳細な説明があります。これらと被る箇所については説明を省いておりますので、そちらをご参照下さい。※要件6~8に対応した説明は末尾に追記しました。

ファイルリスト指定(set list=zip_pathlist.txt)

処理対象のファイルリストは、閲覧権限付与ツールの作業ログから作成しました。ファイル名は作業ログにフルパスで出力されるので、そのまま活用しました。今回は処理ファイルが全てzipファイルの為、それを明示してファイル名は「zip_pathlist.txt」としました(図1#1)。ファイルリストは、本バッチと同じフォルダに置くこととします(図1#2)。

図1:本バッチで使うファイルリスト

zipファイルのパスリストの存在チェック

バッチの性質上、zipファイルのパスリスト(zip_pathlist.txt)が無い事態は考えにくいですが、念のため、存在チェック「if not exist %list% (~」をしています。zipファイルのパスリストが無かった場合、「echo zipファイルのパスリストがありません。」にて、その旨エラーメッセージ表示後、「goto ERROR」で、エラーハンドラ(:ERROR)に飛び、処理を抜けます。

以前の出力ファイルが在れば削除

本バッチはcsvファイルを出力します。ファイル名は決め打ちで「workfolder.csv」です。zipファイルの格納先パス/取得先パスを追記する処理を繰り返す為、以前の記録に追記されてしまうと厄介なので、バッチ起動の度、以前の記録は削除「if exist workfolder.csv del workfolder.csv」する事にします。

0パディングした連番の作成

「set num=00!count!」でループ内のカウンタをつかって連番を作成しています。エラー元のzipファイルは、200個前後になる見通しなので、カウンタ値の前に「00」を追加して3桁の連番としました。カウンタはどんどんカウントアップされるので「set num=!num:~-3,3!」で桁数調整しています。「-3」でカウンタ値の後ろから3文字目を指定「3」そこから右側に3文字を取得しています。

次の「set collectPath=!collectdir!\expand!num!」で連番付きフォルダ名を設定しています。処理結果として、特定フォルダ直下に「expand001~」から連番でカウンタ値を0埋めした連番でカウントアップ分、ズラッと並んだフォルダが作成されます。

zipファイルを取得先から格納先にコピー

zipファイルを、取得先から格納先にコピー「copy /y !targetPath! !collectPath!」しています。典型的なcopyコマンドの使い方で「/y」オプションにより、上書き時、確認メッセージを表示しません。

コピーコマンドの成功・失敗を検出する(0以外はエラー)

処理対象の zipファイルの保管場所がは、あちこちのフォルダに散らばっており、多人数が触る場所に放置されているモノもあります。zipファイルの収集作業時までに、ファイルが展開され、元のzipファイルが削除される懸念があったので、コピー時のエラーを検出しています。

copyコマンドの成功・失敗の判定には「ERRORLEVEL」の値を参照します。「ERRORLEVEL」は、終了コードを返す特殊環境変数で、直前に使用したコマンドの終了コードの確認に利用しています。

「if !ERRORLEVEL! equ 0 (~」にて「ERRORLEVEL」が「0」でない場合、エラーと判定します。「equ」は「==」と同様の比較演算子で、数値を比較するときに使用します。また、遅延環境変数の展開を考慮し「!」で値参照(!ERRORLEVEL!)を行っています。

ここでは、コピーコマンドのステイタスを示す変数(copyST)に、copy成功時「success」と設定「set copyST=”success”」し、copy失敗時「error」と設定「set copyST=”error”」しています。

フルパス情報から取得先フォルダパスを取得

zipファイルの取得先パスを、csvファイルに出力する為「set targetPath=%%~dpa」で、参考サイトの構文、%~dp[変数] (%[変数]からドライブレターを含むフルパスを出力)に従い、フルパス情報から取得先フォルダパスを取得しています。

フルパス情報は「set targetPath=%%a」にて「[変数]a」にいったん格納しています。※定石に従い、ループ内のアルファベッド1文字参照を経由しないと「%~dp[変数] 」構文は期待通りに動かないようです。

zipファイルについて作業フォルダパスを、csvファイル出力

本バッチでは、対象zipファイルの作業フォルダパスを「echo !collectPath!,!targetPath!,!copyST! >> workfolder.csv」にて「格納先パス,取得先パス,コピー状態」の順で処理行数分、csvファイルに出力します。出力したcsvファイルは、格納フォルダ下に収集したzipファイルを解凍する、次のステップのバッチ処理で使われる予定です。

ループ内カウンタのカウントアップ

カウンタで連番を作る為、ループ内で「set /a count+=1」にてカウントアップしています。「/a」オプションにより、値を数式として評価、コマンドライン上で簡単な数式計算が可能になります。

エラーハンドラの設置

本バッチは、エラー判定で処理を進行できない箇所があるため、エラーハンドラ「:ERROR」を設けています。該当するエラー判定後「goto エラーハンドラ名」で、設置したエラーハンドラに飛び、処理を抜ける構成になっています。

文字コードが「UTF-8」の場合

実際にバッチが動作する環境では、そのまま動かすと出力が文字化けしました。そのため、バッチの先頭にchcp」コマンドにより、コマンドプロンプトで使用する文字コードを、あらかじめ変更する処理を挿入しました。今回は「Unicode (UTF-8)」に変更(chcp 65001)する事で、出力時の文字化けを解消できました。普段動かすときは不要のため、コメントアウトする事にしました。

処理ファイルのパスに半角スペースが含まれる場合

本番環境では、処理ファイルのパスに、半角スペースが含まれることがありました。その場合、ファイルパスが途切れて取得されました。これを回避するため、for文に「“tokens=* usebackq”」を追記してファイルリストの読込みを行いました。「tokens=*」で行単位読み出しを指定し「usebackq」は、空白が含まれるファイル名の、読込みを行う時のオプション指定です。

さらにパス文字列は、ダブルクォーテーションで囲んで、ファイル名指定を行う必要がありました。for文内では、上手く修飾できなかったので「set targetPath=”%%a”」で変数に格納時に、ダブルクォーテーションを付加することで、パスとして認識されるようになりました。

ファイルリスト(zip_pathlist.txt)に、半角スペースを含む処理パスを追記

処理対象のファイルリストにも、半角スペースを含む処理パスを追記しておきます(追加図1緑枠)。

取得したzipファイル名を取得

本番環境で動作確認した時に、追加された仕様に伴い、取得したzipファイル名を記録する必要があったため、変数を設けてファイル名取得処理「set targetFile=%%~nxa」を追加しました。続く「csvファイル出力」に、zipファイルの末尾に「~,ファイル名」を追記しました。

動作確認

テスト環境の事前準備

バッチ本体(collect_listfiles.bat)とファイルリスト(zip_pathlist.txt)は、下図の通り「C:\work01」に配置しました(図2赤枠)。

図2:バッチ本体(collect_listfiles.bat)とファイルリスト(zip_pathlist.txt)の配置

ファイルリスト(zip_pathlist.txt)の内容は、前述の「図1#1」の通りとしました。取得先ファイルは下図のように配置しました(図3)。

半角スペースを含む取得先ファイルの準備

ファイルリスト(zip_pathlist.txt)に追記した、半角スペースを含む取得先ファイル(追加図1緑枠)については、下図のように配置しました(追加図2)。

追加図2:ファイルリスト(zip_pathlist.txt)に追記した、半角スペースを含む取得先ファイル

テスト

テスト環境でのバッチ処理

バッチ配置場所(C:\work01)を開き、バッチ本体(collect_listfiles.bat)をダブルクリックで起動します。ファイルリスト(zip_pathlist.txt)が上から順に読込まれ下図のようなDOS画面が表示されて、処理が終了しました(図4)。後から追加した、半角スペースを含む取得先ファイル(追加図2)も取得できました(図4黄枠)。

図4:バッチ(collect_listfiles.bat)処理終了時のDOS画面

バッチ配置場所(C:\work01)を確認します。バッチ処理により、取得したzipファイルの格納フォルダ(collect)と、作業フォルダパスの出力ファイル(workfolder.csv)が作成されました(図5)。

図5:バッチ処理により、取得したZIPファイルの格納フォルダ(collect)と作業フォルダパスの出力ファイル(workfolder.csv)が作成される

テスト結果確認

zipファイルの格納フォルダ(collect)の確認

zipファイルの格納フォルダ(collect)配下を確認します。ファイルリスト(zip_pathlist.txt)の処理行数(4行)分、連番で3フォルダ(expand + 001~003)作成され(図6赤枠)ました。後から追加した、半角スペースを含む取得先ファイル(追加図2)分も、追加で「expand004」フォルダが作成され取得できました(図6黄枠)。

図6:zipファイルの格納フォルダ(collect)配下のフォルダ構成

取得先ファイルが有る場合(図3#1)と無い場合(図3#2)の、バッチの出力結果を確認する為、expand001(取得ファイル有)とexpand002(取得ファイル無)フォルダ配下を確認しました。expand001フォルダ配下には、取得先からコピーしたzipファイルが格納されていて、期待通りの出力が確認できました(図7)。

図7:expand001フォルダ(取得ファイル有)の配下の確認

expand002フォルダ配下は、取得先のzipファイルがないため、何も格納されませんでした。エラーパタンとして期待通りの状態でした(図8)。

図8:expand002(取得ファイル無)配下の確認

バッチ処理に追加した、半角スペースを含むファイルの出力結果を確認する為、expand004フォルダ配下を確認しました。expand004フォルダ配下には、取得先からコピーした、半角スペースを含むzipファイルが格納されていて、期待通りの出力が確認できました(追加図3)。

追加図3:バッチ処理に追加した、半角スペースを含むzipファイルが、格納されていることを確認
作業フォルダパスの出力ファイル(workfolder.csv)の確認

作業フォルダパスの出力ファイル(workfolder.csv)を開きます。追加項目の「ファイル名」が追記されている事を確認します(図9黄枠)。対象zipファイル収集バッチ(collect_listfiles.bat)のコピー処理で使用した「格納先パス、取得先パス、コピー状態、ファイル名」が、この順で、csvファイル出力されている事を確認します(図9)。

図9:作業フォルダパス出力ファイル(workfolder.csv)の出力内容

図9赤枠の2行が、前述で確認したテスト環境でのバッチ処理についての出力です。1行目は格納先パス(図7赤枠フォルダ)、取得先ファイルパス(図3#1フォルダ)、コピー状態(コピー成功→success)と出力、2行目は格納先パス(図8赤枠フォルダ)、取得先ファイルパス(図3#2フォルダ)、コピー状態(コピー失敗→error)と出力され、期待通り処理順に記録される事が確認できました(図9)。図9緑枠の1行に、半角スペースを含むファイル情報(追加図2)についても前述のデータと同様に記録されていることを確認できました。

エラー処理の確認

バッチ本体(collect_listfiles.bat)を単独で作業フォルダ下に置き、ダブルクリックで起動します(図10赤枠)。

図10:collect_listfiles.bat を単独で作業フォルダ下に置き、起動する

ファイルリスト(zip_pathlist.txt)が無い場合、処理を進行できないので、その旨エラー表示され(図11赤枠)処理を抜けます。期待通りの挙動を確認できました。

図11:ファイルリスト(zip_pathlist.txt)が無い場合、その旨エラー表示して処理を抜ける

参考文献

コメント

タイトルとURLをコピーしました