Windowsマシンで作業を行っていると、ツールを作成する事があると思います。
アプリとして作る必要がある場合は、C++やC#等で、Windowsプログラムを作成するでしょう。
そこまでしっかりしたものでなく、ちょっとしたスクリプトを、WindowsOSで用意したい場合には、バッチファイルを作成する機会も多いと思います。
久々に、batファイルを作成した際に、曖昧な点も多く、調べながら作業した為、振り返りのつもりで記事にします。
やりたかった事
今回やりたかった事は、特定の期間に更新されたファイルの一覧を取得するという事になります。
ツールを作成する上での仕様は、以下の要件が満たせるものとしました。
- 特定の期間は、定数または入力によって、変更可能
- ファイルの一覧は、ディレクトリ込みのファイルのみのリストを作成し、重複は除外
- batファイル単体で機能するようにして、SVNの管理化のディレクトリであれば、そこを作業のルートディレクトリとして、どこでも実行可能なものとします
ファイルが残っていたら削除
set TMP_HOGE=確認したいファイル
if exist %TMP_HOGE% del %TMP_HOGE%
既に出力ファイルが作成されていないかチェックし、存在していた場合は削除します。
SVNのリストをログを取ってくる
SVNのログを取得するには、以下のコマンドで行います。
svn log --incremental -r {開始日時}:{終了日時} -v リポジトリのURL
『svn log』を実行し、オプションで『SVNのリポジトリ』『取得する期間』『 --incremental -r -v』を付けています。
取得する期間は、時間だけでなく、リビジョン番号にもできます。
また、『開始日時』から『終了日時』に向かってログを取得してくる為、左右の引数を入れ替える事で昇順・降順も制御出来ます。
ローカルスコープを使う
setlocal enabledelayedexpansion
ローカルスコープでやる処理
endlocal
今回、詰まったポイントでした。
ローカルスコープを使用する必要があった為、使用しています。
下のループ内では、グローバル変数か遅延環境変数でないと、変数が使用できませんでした。
注意点として、ローカルスコープ内では、『%value%』のように、『%』では変数にアクセス出来ない為、『!value!』のように、『!』で囲みます。
『!』で囲れたものは、遅延環境変数と呼びます。
ファイルから1行読み出し
for /f "delims=" %%a in (%TMP_SVN_LOG%) do (
echo %%a
)
for文でファイルの中身を読み取っています。
これは、SVNのログを一旦、ファイルに書き出し、そのファイルの中身を解析するという事をやっています。
1行での読み出したかった為、『"delims="』のように、『delims』に何も設定していません。
『delims』の設定の仕方によって、スペースを区切り文字として引数を取得する事も可能です。
こちらの記事では、具体例も載っている為、必要な実装でお試しください。
文字列にキーワードが入っているか判定する
echo "%%a" | find "%キーワード%" > nul
if ERRORLEVEL 1 (
print キーワードが含まれていない
) else (
print キーワードが含まれている
)
特定の文字列に目的のキーワードが含まれているかチェックします。
今回の場合は、最終的に更新ファイルのリストが欲しかった為、なるべくキーワード余計な行は除外しました。
数字の比較
if !value1! equ 1 (
print 真
) else (
print 偽
)
ここも少し、迷いました。
文字と数字の比較は、書き方が異なります。
文字場合は、『==』で比較してください。
数字の場合は、『equ』や『equ』を使用する必要があります。
サブルーチンの呼び出し
rem サブルーチン呼び出し
call :STRIP !global_string!
:STRIP
SET global_string=%*
exit /b
実は、サブルーチンもほとんど使った事が無かった為、何回か試しました。
Forループ内で、GOTO文を使用した際に、上手く動作しませんでしたが、サブルーチンは使用可能でした。
こちらを参考にさせていただきました。
サブルーチンは引数を渡す事ができます。
以上の事を踏まえて、実際に作成してみたbatが次の項目です。
実際のコード(コードを見て確認したい人向け)
ファイル名やSVNのディレクトリは、任意に変更してください。
おまじないも追加しています。
取得期間とかは、ユーザの入力にしてしまった方が、ユーザビリティは高いかもしれません。
@echo off
cd /d %~dp0
rem 定数(変更禁止)
set TMP_SVN_LOG=中間の出力ファイル名①
set TMP_LOOP=中間の出力ファイル名②
set OUTPUT_FILE=出力ファイル名
set SVN_REPOSITORY=任意のSVNのリポジトリ
rem キーワード文字は、任意に変更してください
set ExclusionKeyword1=Changed
set ExclusionKeyword2=------------------------------------------------------------------------
rem 任意の期間入力してください
set DATE_START=2023-01-01
set DATE_END=2023-12-31
rem
rem SVNのログ取得
rem
if exist %TMP_SVN_LOG% del %TMP_SVN_LOG%
svn log --incremental -r {%DATE_START%}:{%DATE_END%} -v %SVN_REPOSITORY% >> %TMP_SVN_LOG%
rem
rem キーワードで不要なログを除外
rem
if exist %TMP_LOOP% del %TMP_LOOP%
setlocal enabledelayedexpansion
for /f "delims=" %%a in (%TMP_SVN_LOG%) do (
set error1=1
echo "%%a" | find "%ExclusionKeyword1%" > nul
if not ERRORLEVEL 1 (
set error1=0
)
set error2=1
echo "%%a" | find "%ExclusionKeyword2%" > nul
if not ERRORLEVEL 1 (
set error2=0
)
if !error1! equ 1 (
if !error2! equ 1 (
echo %%a >> %TMP_LOOP%
)
)
)
endlocal
rem
rem 重複を削除する
rem
if exist %OUTPUT_FILE% del %OUTPUT_FILE%
set global_string=""
setlocal enabledelayedexpansion
set n=0
for /f "delims=" %%a in (%TMP_LOOP%) do (
set /a n=!n!+1
set m=0
set write=1
for /f "delims=" %%b in (%TMP_LOOP%) do (
set /a m=!m!+1
if !m! lss !n! (
if %%a == %%b ( set write=0 )
)
)
set global_string=%%a
rem サブルーチン呼び出し
call :STRIP !global_string!
rem SVNのアクションのログのみ書き出し
set result=!global_string:~0,1!
set true_flag=0
if !result!==A set true_flag=1
if !result!==M set true_flag=1
if !result!==D set true_flag=1
if !true_flag! equ 0 (
set write=0
)
rem 書き込み判定
if !write! equ 1 (
echo !global_string! >> %OUTPUT_FILE%
)
)
rem
rem サブルーチンで先頭と文末の開業を削除
rem ※サブルーチンの下に処理を書く際は注意
rem
:STRIP
SET global_string=%*
exit /b
endlocal