昨日、の続き。
昨日書いた、:getline サブルーチンだが、「空行とEOFを区別できない」という set /p コマンドの仕様からああいう機能になってしまった。しかし、実際 tail コマンドを書こうとするとどうも使いにくい。そこで、まず普通の1行入力機能を持ったサブルーチンに書き直した。どうしても状態を覚えておく必要があるので、固有の環境変数を2個使う。
:getline if not defined __D ( set __N=0 :g1 set __D= set /p __D= if not defined __D ( if !__N! geq 100 ( set __N= set %1= set %2=1 goto :eof ) set /a __N=__N+1 goto g1 ) ) if %__N% gtr 0 ( set %1= set /a __N=__N-1 ) else ( set __N= set %1=!__D! set __D= ) set %2=0
ラベルg1の次の set __D= はこのロジックでは不要のはずだが、set /p を使う時はペアで必ず書くことにした。
機能は、
- 第一引数に入力行が返る。ただし空行ならnot definedとなる
- 第二引数は EOF なら 1 が返る。EOF でなければ 0 が返る
- 初回呼び出しの時点で、環境変数 __D が未定義であること
制約は昨日のと同じ。
これを使って、tailコマンドは、
@echo off setlocal enabledelayedexpansion set __D= if not defined LINE set LINE=20 if not exist "%~1" exit /b 1 call :tail < %1 goto :eof :tail set I=-%LINE% set J=0 :loop call :getline DATA EOF if "%EOF%" == "1" goto end set /a J=J+1 set SAVE%J%=!DATA! set /a I=I+1 set SAVE%I%= goto loop :end set /a I=I+1 if %I% leq 0 set I=1 for /l %%W in (%I%,1,%J%) do ( if defined SAVE%%W (echo !SAVE%%W!) else echo. ) goto :eof <<この後にさっきの:getlineのコードが入る>>
と書けた。ファイルが長いとそれなりに時間がかかるが、途中でうっかりコントロールCを押さないように。(昨日の注意書きを参照)