というわけで、バッチのマルチスレッド化に着手してみた。
基本コンセプトは、バッチ処理をキュー化し、キューを処理するバッチを並列で起動する。
バッチは、すべて windows 標準機能だけで動作するようになっている。
Java とか使えば、もっとシンプルになるのだが、バッチでやることに意味があるのだよ。フッフッフ・・・
【設定ファイル】
set GIMP_CMD="C:\appli_x86\GIMP-2.0\bin\gimp-console-2.6.exe" @rem キューディレクトリ set QUEUE_DIR=%~dp0.queues @rem 処理対象ファイル拡張子 set TARGET_FILES=*.jpg @rem 上下左右均等切り取りサイズ(ピクセル) set CROP_SIZE=10 @rem 対象イメージ中の最小サイズに合わせて切り取るフラグ @rem 以下2つは、断裁時の切断長を補正する目的で利用する。 @rem set ADJUST_SIZE_FLAG=TRUE set ADJUST_SIZE_FLAG=FALSE @rem 最小サイズに合わせて切り取る場合に偶数ページは左側、奇数ページは右側を切るフラグ set ODD_PAGE_ADJUST_LEFT-SIDE_FLAG=TRUE @rem 画像の拡大縮小(1.0で何もしない) @rem set SCALE_RATE=0.25 @rem set SCALE_RATE=0.5 set SCALE_RATE=0.75 @rem set SCALE_RATE=1.0 exit /b
【キューを処理するバッチ】
キューを処理するバッチのポイントは、キューがない場合、ping を使って5秒間待つこと。
@echo off @rem キューディレクトリに登録されたバッチファイルを実行するバッチ setlocal @rem 引数チェック if "%~1"=="" goto usage set SCRIPT_DIR=%~dp0 :loop1 @rem 処理対象キューディレクトリ set QUEUE_DIR=%~1 dir "%QUEUE_DIR%" /A:D >nul 2>&1 if not errorlevel 1 goto mainStart @rem エラーの場合 @echo "%QUEUE_DIR%" が存在しないか、ディレクトリではありません。 1>&2 goto finish :mainStart @rem キューディレクトリのバッチファイル件数を取得 set fileCount=0 for %%I in (%QUEUE_DIR%\*.bat) do set /a fileCount=fileCount+1 if %fileCount% equ 0 ( @rem キューディレクトリのバッチファイルが無い場合 5 秒間ウェイトする ping -n 5 localhost > nul goto mainStart ) @rem キューディレクトリのバッチファイルをファイル名昇順で処理 for /f "tokens=*" %%I in ('dir /A:-D /B /O:N "%QUEUE_DIR%\*.bat"') do ( call :executeBat "%QUEUE_DIR%\%%I" ) goto mainStart :executeBat set TARGET_BAT=%~1 @echo "%TARGET_BAT%" start @rem キューディレクトリのバッチを実行 call "%TARGET_BAT%" @rem 処理済のバッチファイルを削除 del "%TARGET_BAT%" exit /b @rem 使用法表示 :usage @echo 使い方:%~nx0 ^<dir^> @echo 指定されたキューディレクトリのバッチファイルを処理する。 @echo キューディレクトリに格納するファイルは、バッチファイルとする。 :finish @echo 処理を完了しました。
【キューを処理するバッチを起動するバッチ】
キューディレクトリが、存在しない場合は、キューディレクトリを作成する。
全てのキューディレクトリに対して、キューを処理するバッチを、start コマンドで起動している。
@echo off @echo キュー処理開始バッチ setlocal @rem キュー並行処理数 set QUEUE_CONSUMER_COUNT=4 set SCRIPT_DIR=%~dp0 call "%SCRIPT_DIR%property.bat" dir "%QUEUE_DIR%" /A:D >nul 2>&1 if not errorlevel 1 goto mainStart @rem キューディレクトリが存在しない場合 mkdir "%QUEUE_DIR%" :mainStart @rem 全てのキューディレクトリに対して、キュー処理バッチを起動 for /L %%I in (1,1,%QUEUE_CONSUMER_COUNT%) do ( dir "%QUEUE_DIR%\queue%%I" /A:D >nul 2>&1 @rem キューディレクトリが無い場合は新規作成 if errorlevel 1 mkdir "%QUEUE_DIR%\queue%%I" @rem キュー処理バッチを起動 start "queue%%I" cmd /c "%SCRIPT_DIR%queue-consumer.bat" %QUEUE_DIR%\queue%%I ) goto finish :finish @echo 処理を完了しました。
【キューを登録するバッチ】
キューを登録するバッチは、結構ヘビーになっている。
要点を列挙する。
- キューファイルは5桁の連番
- キュー登録数の少ないキューディレクトリにキューを追加
- 実行したいバッチを実行するバッチをキューファイルとして作成
@echo off @echo 自炊用フィルタランチャー setlocal enabledelayedexpansion set SCRIPT_DIR=%~dp0 call "%SCRIPT_DIR%property.bat" @rem 引数チェック if "%~1"=="" goto usage set SCRIPT_DIR=%~dp0 set LOG_FILE=%SCRIPT_DIR%\launcher.log set COLOR_FILTER_ARGS= set MONO_FILTER_ARGS= set OCR_FILTER_ARGS= @rem 全ての引数をループ処理する :loop1 if "%~1"=="" goto finish @rem まだ引数がある場合 dir "%~1" /A:D >nul 2>&1 if not errorlevel 1 goto dirCheckOk @rem エラーの場合 @echo "%~1" が存在しないか、ディレクトリではありません。 2>>%LOG_FILE% 1>&2 shift goto loop1 :dirCheckOk set COLOR_DIR=%~1\color set COVER_DIR=%~1\cover set MONO_DIR=%~1\mono set OCR_DIR=%~1\ocr set COLOR_FILTER_ARGS="" set COVER_FILTER_ARGS="" set MONO_FILTER_ARGS="" set OCR_FILTER_ARGS="" @rem color ディレクトリの存在をチェック dir /A:D "%COLOR_DIR%" >nul 2>&1 if errorlevel 1 goto coverDir @rem *.jpg ファイルの存在をチェック dir /A:-D "%COLOR_DIR%\*.jpg" >nul 2>&1 if errorlevel 1 goto coverDir :colorDir @rem color ディレクトリが存在する場合 set COLOR_FILTER_ARGS="%COLOR_DIR%" :coverDir @rem cover ディレクトリの存在をチェック dir /A:D "%COVER_DIR%" >nul 2>&1 if errorlevel 1 goto monoDir @rem *.jpg ファイルの存在をチェック dir /A:-D "%COVER_DIR%\*.jpg" >nul 2>&1 if errorlevel 1 goto monoDir @rem cover ディレクトリのパスを設定 set COVER_FILTER_ARGS="%COVER_DIR%" :monoDir @rem mono ディレクトリの存在をチェック dir /A:D "%MONO_DIR%" >nul 2>&1 if errorlevel 1 goto ocrDir @rem *.jpg ファイルの存在をチェック dir /A:-D "%MONO_DIR%\*.jpg" >nul 2>&1 if errorlevel 1 goto ocrDir @rem mono ディレクトリのパスを設定 set MONO_FILTER_ARGS="%MONO_DIR%" :ocrDir @rem ocr ディレクトリの存在をチェック dir /A:D "%OCR_DIR%" >nul 2>&1 if errorlevel 1 goto doFilter @rem *.jpg ファイルの存在をチェック dir /A:-D "%OCR_DIR%\*.jpg" >nul 2>&1 if errorlevel 1 goto doFilter @rem ocr ディレクトリのパスを設定 set OCR_FILTER_ARGS="%OCR_DIR%" :doFilter @rem キュー追加先ディレクトリ、ファイル名およびテンポラリファイル名を取得 call :getNewQueueFilename @rem カラーフィルター if "%COLOR_FILTER_ARGS:"=%"=="" goto coverFilter @rem テンポラリバッチファイルにカラーフィルターバッチ処理を追加 @echo "%SCRIPT_DIR%color-filter.bat" %COLOR_FILTER_ARGS% >> %newQueuePath%%tempQueueFileName% @rem テンポラリバッチファイルをリネーム rename %newQueuePath%%tempQueueFileName% %newQueueFileName% @rem キュー追加先ディレクトリ、ファイル名およびテンポラリファイル名を取得 call :getNewQueueFilename :coverFilter @rem カバーフィルター if "%COVER_FILTER_ARGS:"=%"=="" goto monoFilter @rem テンポラリバッチファイルにカバーフィルターバッチ処理を追加 @echo "%SCRIPT_DIR%cover-filter.bat" %COVER_FILTER_ARGS% >> %newQueuePath%%tempQueueFileName% @rem テンポラリバッチファイルをリネーム rename %newQueuePath%%tempQueueFileName% %newQueueFileName% @rem キュー追加先ディレクトリ、ファイル名およびテンポラリファイル名を取得 call :getNewQueueFilename @rem モノトーンフィルター :monoFilter if "%MONO_FILTER_ARGS:"=%"=="" goto ocrFilter @rem テンポラリバッチファイルにモノトーンフィルターバッチ処理を追加 @echo "%SCRIPT_DIR%monotone-filter.bat" %MONO_FILTER_ARGS% >> %newQueuePath%%tempQueueFileName% @rem テンポラリバッチファイルをリネーム rename %newQueuePath%%tempQueueFileName% %newQueueFileName% @rem キュー追加先ディレクトリ、ファイル名およびテンポラリファイル名を取得 call :getNewQueueFilename @rem OCR フィルター :ocrFilter if "%OCR_FILTER_ARGS:"=%"=="" goto filterEnd @rem テンポラリバッチファイルにOCR フィルターバッチ処理を追加 @echo "%SCRIPT_DIR%ocr-filter.bat" %OCR_FILTER_ARGS% >> %newQueuePath%%tempQueueFileName% @rem テンポラリバッチファイルをリネーム rename %newQueuePath%%tempQueueFileName% %newQueueFileName% :filterEnd shift goto loop1 @rem ------------------------------------------------------ @rem キューを作成するフルパスのファイル名およびテンポラリファイル名を取得 @rem 最もキュー残数の少ないディレクトリが対象となる。 @rem newQueuePath : 新キューパス(戻り値) @rem newQueueFileName : 新キューファイル名(戻り値) @rem tempQueueFileName : テンポラリキューファイル名(戻り値) :getNewQueueFilename @rem キュー待ち数が少ないディレクトリを取得 set minFileCount=99999 for /f "tokens=*" %%I in ('dir /A:D /B /O:N "%QUEUE_DIR%"') do ( call :getTargetQueueDir "%QUEUE_DIR%\%%I" ) @rem 最終キューファイル名を取得 set lastQueueFilename= for /f "tokens=*" %%I in ('dir /A:-D /B /O:-N "%targetQueueDir%\*.bat" 2^>nul') do ( set lastQueueFileBaseName=%%~nI @rem set lastQueueFileExtention=%%~xI goto forend1 ) :forend1 set lastFileBaseNameIn=%lastQueueFileBaseName% call :getNextNumberFileName @rem 新キューパス set newQueuePath=%targetQueueDir%\ @rem 新キューファイル名 set newQueueFileName=%nextFileBaseNameOut%.bat @rem テンポラリキューファイル名 set tempQueueFileName=%nextFileBaseNameOut%.bat.tmp exit /b @rem ------------------------------------------------------ @rem キュー作成先ディレクトリを取得 @rem minFileCount : キュー数の最小値(引数) @rem targetQueueDir : キュー作成先ディレクトリ(戻り値) :getTargetQueueDir @rem ディレクトリ中のファイル数を取得 set fileCount=0 for %%J in ("%~1\*") do set /a fileCount=fileCount+1 @rem キュー待ち数が最も少ないディレクトリを検索 if %fileCount% lss %minFileCount% ( @rem キュー数の最小値を更新 set minFileCount=%fileCount% @rem キュー作成先ディレクトリ set targetQueueDir=%~1 ) exit /b @rem ------------------------------------------------------ @rem 最終番号+1を返す @rem 番号は、5桁の先頭0埋めで返す @rem lastFileBaseNameIn : 最後のファイル名(引数) @rem nextFileBaseNameOut : 次のファイル名(戻り値) :getNextNumberFileName @rem パディング文字 set PAD_ZERO=00001 @rem 番号の桁数 set FIXD_LENGTH=5 if "%lastFileBaseNameIn%"=="" ( @rem 入力値なしの場合 set nextFileBaseNameOut=%PAD_ZERO% exit /b ) @rem 最終番号+1を算出 set /a nextFileBaseNameOut=%lastFileBaseNameIn%+1 @rem 番号の桁数を取得 set length=1 set strtmp=%nextFileBaseNameOut% :looplen1 if "%strtmp:~0,-1%"=="" goto nextlen1 set strtmp=%strtmp:~0,-1% set /a length=length+1 goto looplen1 :nextlen1 @rem パディング文字から番号の桁数除いたものと番号を結合 set nextFileBaseNameOut=!PAD_ZERO:~0,-%length%!%nextFileBaseNameOut% exit /b @rem ------------------------------------------------------ @rem 使用法表示 :usage @echo 使い方:%~nx0 ^<dir^> @echo 指定のディレクトリ以下の color cover mono ocr ディレクトリをそれぞれ対応するフィルターで処理を行う。 :finish @echo 自炊用フィルタ処理が完了しました。何かキーを押してください。 pause > nul