How to: Get the current line number
Posted: 14 May 2015 07:50
I build a new technic to detect the current line number in a batch file while it's running.
Something like
2011 I found a solution that uses findstr to search for an unique token in the file
SO: How to get the current line number?
So you have to help the function with appending a token
I didn't like this, as you can't copy & paste blocks without modifying the tokens.
But this thuesday, I finally build a solution that doesn't need a token, but it's much more complicated.
It uses self modifying of the batch file two times.
I will enumerate the steps
1) Call the :GetLineNumber function and copy the current batch file to <filename>.tmp for later restore
2) Replace the complete current file with macros and a special function at the end of the file
3) exit the :GetLineNumber function, this will result in returning to a line with a macro
4) The macro sets the line number and file position to a variable and GOTO to the special function
5) The special function replaces again the complete file with a label (:#) at the beginning, one copy macro and placeholders of the exact size of the file position
6) The special function exists by GOTO to the start label (:#)
7) The macro replaces the file again by copying and deleting the backup <filename>.tmp to the current file
After the macro block is finished the file position is exactly at the same position as it was after step 1
That's all
That's also the cause why it doesn't work inside code blocks and also in a FOR one liner it doesn't work very well.
Here are the two intermediate files.
And the code of the complete code is
Something like
Code: Select all
call :GetCurrentLineNumber lineNr
echo The line number is %lineNr%
2011 I found a solution that uses findstr to search for an unique token in the file
SO: How to get the current line number?
So you have to help the function with appending a token
Code: Select all
call :GetCurrentLineNumber lineNr myUniqueToken471234
echo The line number is %lineNr%
call :GetCurrentLineNumber lineNr myUniqueToken4572634
echo The line number is %lineNr%
I didn't like this, as you can't copy & paste blocks without modifying the tokens.
But this thuesday, I finally build a solution that doesn't need a token, but it's much more complicated.
It uses self modifying of the batch file two times.
I will enumerate the steps
1) Call the :GetLineNumber function and copy the current batch file to <filename>.tmp for later restore
2) Replace the complete current file with macros and a special function at the end of the file
3) exit the :GetLineNumber function, this will result in returning to a line with a macro
4) The macro sets the line number and file position to a variable and GOTO to the special function
5) The special function replaces again the complete file with a label (:#) at the beginning, one copy macro and placeholders of the exact size of the file position
6) The special function exists by GOTO to the start label (:#)
7) The macro replaces the file again by copying and deleting the backup <filename>.tmp to the current file
After the macro block is finished the file position is exactly at the same position as it was after step 1
That's all
That's also the cause why it doesn't work inside code blocks and also in a FOR one liner it doesn't work very well.
Here are the two intermediate files.
Code: Select all
%#:#=0,0%...............................
%#:#=4,41%....
%#:#=5,56%...................
%#:#=7,86%....
%#:#=8,101%.................
%#:#=12,130%....................
%#:#=13,163%.............................
%#:#=14,205%..
:getLineNumber
set /a repeat+=1
exit /b
:calc
(
setlocal EnableDelayedExpansion
FOR /F "tokens=1,2 delims=," %%A in ("!offset!") DO set /a line=%%A,label=%%B
set /a size=2720
set L=^
for /L %%n in (1 1 13) DO set "L=!L:~0,4000!!L:~0,4000!"
set /a remainSize=label-11, blocks=remainSize/8002, remainSize=remainSize-8002*blocks-2
if !remainSize! LSS 2 set /a remainSize+=8002, blocks-=1
FOR /F %%R in ("!remainSize!") DO set "lastBlock=!L:~-%%R!)"
(
(echo :#)
(echo (%%#%%)
FOR /L %%n in (1 1 !blocks!) DO (echo(!L!)
(echo(!lastBlock!)
echo(
) > "F:\getLineNum.bat"
set "#=endlocal & copy "F:\getLineNum.bat.tmp" "F:\getLineNum.bat" > NUL & set /a lineNumber=!line! & call :magicEcho"
goto :#
)
Code: Select all
:#
(%#%
)
And the code of the complete code is
Code: Select all
@echo off
call :getLineNumber result
echo %result%
call :getLineNumber result
echo %result%
for %%G in ( 1 2 3) DO call :getLineNumber result
echo %result%
exit /b
:GetLineNumber
setlocal EnableDelayedExpansion
for %%F in ("%~f0") do set /a size=%%~zF
set "empty=."
for /L %%n in (1 1 13) DO set "empty=!empty:~0,4000!!empty:~0,4000!"
set LF=^
REM ***
copy "%~f0" "%~f0.tmp" > NUL
set /a blockSizeHalf=4000, blockSize2=blockSizeHalf*2+2
set /a charCount=0, lastOffset=0, startLine=0, preChars=0, lineNr=0, cnt=0
for /F "usebackq delims=:" %%O in (`findstr /o "^" "%~f0"`) DO (
set "offset=%%O"
set /a lineNr+=1
set /a diff=offset-lastOffset, lastOffset=offset
set /a charCount+=diff
if !diff! GEQ 12 (
set "output=%%#:#=!startLine!,!preChars!%%"
set "s=!output!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
set /a fill=charCount-len-1
for /F %%L in ("!fill!") DO set "output=!output!!empty:~0,%%L!!LF!"
set /a cnt+=1
set "line!cnt!=!output!"
set /a charCount=0, startLine=LineNr, preChars=offset
)
)
(
(
for /L %%n in (1 1 !cnt!) DO (
<nul set /p ".=!line%%n!"
)
(echo()
echo :getLineNumber
echo set /a repeat+=1
echo exit /b
echo :calc
echo (
echo setlocal EnableDelayedExpansion
echo FOR /F "tokens=1,2 delims=," %%%%A in ("^!offset^!"^) DO set /a line=%%%%A,label=%%%%B
echo copy "%~f0.tmp" "%~f0" > NUL
echo set /a size=%size%
echo set L=^^
(echo()
(echo()
echo for /L %%%%n in (1 1 13^) DO set "L=^!L:~0,%blockSizeHalf%^!^!L:~0,%blockSizeHalf%^!"
echo set /a remainSize=label-11, blocks=remainSize/%blockSize2%, remainSize=remainSize-%blockSize2%*blocks-2
echo if ^^!remainSize^^! LSS 2 set /a remainSize+=%blockSize2%, blocks-=1
echo FOR /F %%%%R in ("^!remainSize^!"^) DO set "lastBlock=^!L:~-%%%%R^!)"
echo (
echo (echo :#^)
echo (echo (%%%%#%%%%^)
echo FOR /L %%%%n in (1 1 ^^!blocks^^!^) DO (echo(^^!L^^!^)
echo (echo(^^!lastBlock^^!^)
echo echo(
echo ^) ^> "%~f0"
echo set "#=endlocal & copy "%~f0.tmp" "%~f0" > NUL & set /a %1=^!line^!-1"
echo goto :#
echo ^)
) > "%~f0"
REM Jump out of cache and return, here we lost the parameters
endlocal
set /a repeat=1
set "#=<nul set offset=# & goto :calc "
exit /b
)
exit /b