業務で、処理エラーログに出力されたファイルを収集するバッチを、作る事になりそうなので試作してみました。
- バッチ作成の経緯
- DOSバッチ化したい事
- 作成したDOSバッチ
- コマンド説明
- 動作確認
- 参考文献
バッチ作成の経緯
職場のオフィスファイル等に閲覧権限を付与する作業があり、その際 zipファイルが処理できない為、閲覧権限付与ツールの作業ログから、処理エラーが大量に検出されました。とりあえず zipファイルを一か所に集めて、一括して解凍してから処理する事で対応策がまとまったので、先行してそのようなDOSバッチを作ってみました。
DOSバッチ化したい事
バッチ化したい事をざっくり書き出しました。※現場で動かす機会があり、必要な要件を6~8に追加しました。
- ファイルリストを読込み対象ファイルを取得する
- 閲覧権限付与ツールの作業ログはcsv形式で、処理ファイルはフルパスで出力される
- 作業ログからエラー原因のzipファイルのみ、パス付で抜き出してリスト化する
- 取得ファイルを格納する特定フォルダを作成する
- 収集するzipファイルの上書きを避けるため、特定フォルダ下に連番で命名したフォルダを作成
- 収集するzipファイルは連番で作成したフォルダ下に格納する
- zipファイルの「格納先パス/取得先パス/コピー状態」をcsvファイルに出力する
- 文字コードが「UTF-8」の場合、処理開始時に適用させる
- 処理ファイルのパスに半角スペースが含まれる事を考慮する
- 取得した処理ファイル名も記録する
作成したDOSバッチ
以上を踏まえて、以下の様なDOSバッチ(collect_listfiles.bat)を作成しました。※要件6~8に対応する為、赤字箇所を追記・修正しました。
コマンド説明
バッチの構成は直近で作成したバッチから流用し、基本事項ついては、初回に作成したバッチに詳細な説明があります。これらと被る箇所については説明を省いておりますので、そちらをご参照下さい。※要件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赤枠)。
ファイルリスト(zip_pathlist.txt)の内容は、前述の「図1#1」の通りとしました。取得先ファイルは下図のように配置しました(図3)。
半角スペースを含む取得先ファイルの準備
ファイルリスト(zip_pathlist.txt)に追記した、半角スペースを含む取得先ファイル(追加図1緑枠)については、下図のように配置しました(追加図2)。
テスト
テスト環境でのバッチ処理
バッチ配置場所(C:\work01)を開き、バッチ本体(collect_listfiles.bat)をダブルクリックで起動します。ファイルリスト(zip_pathlist.txt)が上から順に読込まれ下図のようなDOS画面が表示されて、処理が終了しました(図4)。後から追加した、半角スペースを含む取得先ファイル(追加図2)も取得できました(図4黄枠)。
バッチ配置場所(C:\work01)を確認します。バッチ処理により、取得したzipファイルの格納フォルダ(collect)と、作業フォルダパスの出力ファイル(workfolder.csv)が作成されました(図5)。
テスト結果確認
zipファイルの格納フォルダ(collect)の確認
zipファイルの格納フォルダ(collect)配下を確認します。ファイルリスト(zip_pathlist.txt)の処理行数(4行)分、連番で3フォルダ(expand + 001~003)作成され(図6赤枠)ました。後から追加した、半角スペースを含む取得先ファイル(追加図2)分も、追加で「expand004」フォルダが作成され取得できました(図6黄枠)。
取得先ファイルが有る場合(図3#1)と無い場合(図3#2)の、バッチの出力結果を確認する為、expand001(取得ファイル有)とexpand002(取得ファイル無)フォルダ配下を確認しました。expand001フォルダ配下には、取得先からコピーしたzipファイルが格納されていて、期待通りの出力が確認できました(図7)。
expand002フォルダ配下は、取得先のzipファイルがないため、何も格納されませんでした。エラーパタンとして期待通りの状態でした(図8)。
バッチ処理に追加した、半角スペースを含むファイルの出力結果を確認する為、expand004フォルダ配下を確認しました。expand004フォルダ配下には、取得先からコピーした、半角スペースを含むzipファイルが格納されていて、期待通りの出力が確認できました(追加図3)。
作業フォルダパスの出力ファイル(workfolder.csv)の確認
作業フォルダパスの出力ファイル(workfolder.csv)を開きます。追加項目の「ファイル名」が追記されている事を確認します(図9黄枠)。対象zipファイル収集バッチ(collect_listfiles.bat)のコピー処理で使用した「格納先パス、取得先パス、コピー状態、ファイル名」が、この順で、csvファイル出力されている事を確認します(図9)。
図9赤枠の2行が、前述で確認したテスト環境でのバッチ処理についての出力です。1行目は格納先パス(図7赤枠フォルダ)、取得先ファイルパス(図3#1フォルダ)、コピー状態(コピー成功→success)と出力、2行目は格納先パス(図8赤枠フォルダ)、取得先ファイルパス(図3#2フォルダ)、コピー状態(コピー失敗→error)と出力され、期待通り処理順に記録される事が確認できました(図9)。図9緑枠の1行に、半角スペースを含むファイル情報(追加図2)についても前述のデータと同様に記録されていることを確認できました。
エラー処理の確認
バッチ本体(collect_listfiles.bat)を単独で作業フォルダ下に置き、ダブルクリックで起動します(図10赤枠)。
ファイルリスト(zip_pathlist.txt)が無い場合、処理を進行できないので、その旨エラー表示され(図11赤枠)処理を抜けます。期待通りの挙動を確認できました。
参考文献
- なお (2019–)「SEからの脱出日記」”ファイル有無を確認するバッチファイル備忘録” 2023年2月28日閲覧
- 「知識ゼロからのwindowsバッチファイル超入門」”文字列を自在に切り取る” 2023年2月28日閲覧
- Windowsコマンド虎の巻 (2018–)「copy」2023年2月28日閲覧
- Windowsコマンド虎の巻 (2018–)「環境変数」2023年2月28日閲覧
- 「知識ゼロからのwindowsバッチファイル超入門」”errorlevel(終了コードを取得する)” 2023年2月28日閲覧
- CyberAgent, Inc. Amebaブログ akagane「DOS備忘録06 – copyコマンド errorlevel」2023年2月28日閲覧
- そまちょブログ. (2020–) “バッチファイルで条件分岐をするif文の使い方” 2023年2月28日閲覧
- FC2, Inc. FC2ブログ「マコトのおもちゃ箱 ~ぼへぼへ自営業者の技術メモ~」”Windows、バッチファイルでエラーレベルが取れない。(遅延環境変数の展開)“ 2023年2月28日閲覧
- Buzzword Inc.(2006-)「JavaDrive」「文字コードの設定(CHCP)」”CHCPコマンドの使い方“ 2023年7月28日閲覧
- TOWN株式会社(2004-)「クロジカ | テックブログ」”windowsで変数にファイルの中身や実行結果を格納する方法” 2023年7月28日閲覧
- Buzzword Inc.(2006-)「JavaDrive」「テキストファイルの内容を区切り文字でトークンに分解し繰り返し処理を使う(FOR)」”空白が含まれるファイル名を指定する“ 2023年7月28日閲覧
- Yousuke.U「1 NOTES」「AI ANSWERS by 1 NOTES」”コマンドプロンプトでスペースを含むファイル名を指定する方法“ 2023年7月28日閲覧
- Rainbow Engine “バッチでファイルパスやファイル名を取得する方法“ 2023年7月28日閲覧
コメント