How to: Get the current line number

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

How to: Get the current line number

#1 Post by jeb » 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

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
8) After the macro block is finished the file position is exactly at the same position as it was after step 1

That's all :D

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

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to: Get the current line number

#2 Post by dbenham » 14 May 2015 11:43

Truly amazing and wicked code :!: :twisted:

I was oh so close to figuring out the technique by examining your MagicEcho code. I recognized the use of macros (with substitution!), as well as the double temporary replacement of the batch script code. But I couldn't quite see the content of the 2nd replacement, and the fact that the length changes - critical to returning to the correct location in the original. Your use of :# as a label and %#:#=...% as a macro had me confused - I was mistakenly thinking the names of the label and macro were related - you sneaky devil.

Your explanation here helped connect the dots for me. Thanks. :D

Note - your posted intermediate code is mistakenly for MagicEcho instead of GetCurrentLineNumber :wink:

I don't see why you compute LineNr. You can use FINDSTR to get both the lineNr and the offset

Code: Select all

for /F "usebackq tokens=1,2 delims=:" %%A in (`findstr /no "^" "%~f0"`) DO (
You can also completely eliminate the lineNr and offset variables within that loop and simply use %%A and %%B instead.

I would be tempted to improve performance a bit by writing the first intermediate file to a new name only once, at the beginning, and then use MOVE to swap the intermediate with the original as needed.

I also am wondering if you might be able to write a precomputed second intermediate file only once, at the beginning, and then somehow use FIND /F "SKIP..." and/or FINDSTR to quickly get the correct length replacement at run time. I haven't convinced myself whether this idea is viable or not.

Of course you would want to add termination code to delete the intermediate files at the end, which would complicate use of the technique a bit.


Anyway... congratulations on achieving your long sought solution - very impressive.


Dave Benham

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to: Get the current line number

#3 Post by dbenham » 14 May 2015 12:30

Another idea for the 2nd replacement file (I think more practical).

Prepare the file once at the beginning, with a pair of lines for each CALL point (plus additional spacing lines, as needed). Each pair would consist of a :label, followed by a macro:

Code: Select all

:123  additional chars as needed
%#%
The offset at the end of the macro line should correspond to the end of the original CALL line, so the file pointer is at the correct position. The end of the :calc routine would use goto :!line! instead of goto :#. This would enable the 2nd intermediate file to be constant, and MOVE could be used to swap it in and out as needed.

Since MOVE within the same folder is nearly instantaneous, performance should be greatly improved. All file preperation is done only once during initialization, and subsequent getLineNumber (or MagicEcho) calls are very fast.


Dave Benham

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: How to: Get the current line number

#4 Post by Aacini » 15 May 2015 23:52

Wow! Your method to get line numbers is very interesting! :D However, it requires several complicated manipulations and the line numbers can not be get inside code blocks.

I devised a different approach to get line numbers that is much simpler, run faster and works at any place. This method consist in generate a copy of the Batch file with the real line numbers replaced at the right places; the new file will have LN letters appended at end of its name. The way to get the current line number is invoking :GetLineNumber subroutine using this format:

Code: Select all

call :getLineNumber varName

... and insert the :GetLineNumber subroutine at end of the file. This is the code:

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 DisableDelayedExpansion

(for /F "delims=" %%a in ('findstr /N "^" "%~F0"') do (
   set "line=%%a"
   setlocal EnableDelayedExpansion
   set "line=!line:*:=!"
   if defined line if "!line::GetLineNumber=!" neq "!line!" (
      if "!line!" neq ":GetLineNumber" (
         set "line=!line:call :getLineNumber =!"
         for /F "delims=:" %%b in ("%%a") do set "line=!line:%1=set %1=%%b!"
      ) else (
         goto break
      )
   )
   echo(!line!
   endlocal
)) > "%~DPN0LN.bat"
:break
exit /B

Antonio

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: How to: Get the current line number

#5 Post by jeb » 16 May 2015 08:21

Aacini wrote:Wow! Your method to get line numbers is very interesting! :D

Thanks, I thought about this for some years.

Aacini wrote:However, it requires several complicated manipulations and the line numbers can not be get inside code blocks.

Yes there are still some nasty limitations, but mainly it was a proof of concept.
And the original idea was to build a call stack dumper.

Your idea is realy simple and probably it works more reliable :)

dbenham wrote:Another idea for the 2nd replacement file (I think more practical).
Prepare the file once at the beginning, with a pair of lines for each CALL point (plus additional spacing lines, as needed). Each pair would consist of a :label, followed by a macro:


I would prefere a solution with only one file at all, so the line retriever code and the labels are in the same file.
Something like

Code: Select all

:45
%#:$=46%
:47
%#:$=48%

dbenham wrote:Since MOVE within the same folder is nearly instantaneous, performance should be greatly improved. All file preperation is done only once during initialization, and subsequent getLineNumber (or MagicEcho) calls are very fast.

Until now I didn't think about speed, as I used the GetLineNumber only once for building error codes for visual studio, something like

Code: Select all

if ... (
   call :GetLineNumber lineNo {3D967145-10B6-44A0-96EF-91B6C6E2DD0E}
   echo %~f0 (!lineNo!^): Warning: Invalid target for production
)

And I wasn't sure that MOVE is also possible way for self modifying code, or does the parser stops when the files is moved away? I simply didn't test it.

dbenham wrote:I don't see why you compute LineNr. You can use FINDSTR to get both the lineNr and the offset

Yes, I knew it but I modified the code so often, that there is much room for improvements.

dbenham wrote: I was mistakenly thinking the names of the label and macro were related - you sneaky devil.

Yes, that was some sort of making the challenge not to easy, also I decided to remove the full function names, as a maigc echo should be at least a bit magic :D

Currently I'm searching for a macro which is also a valid label

Code: Select all

%,:$=47,123%

This is a valid macro with the variable name , (comma) and also a label that can be called via
goto :$
But currently, I found no way to include the numbers to the label.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to: Get the current line number

#6 Post by dbenham » 16 May 2015 09:09

jeb wrote:Currently I'm searching for a macro which is also a valid label

Code: Select all

%,:$=47,123%

Yes, I was thinking the same thing when I saw your MagicEcho, but I also could not get it to work :(


@Aacini - I thought of a similar, strategy, except I use JREPL.BAT to update the line numbers in place within the original script. More on this at the end of this post.


@jeb - I've implemented my ideas for modification to your design, and it works very well (and fast) :D

My version requires a single call to :initGetLineNumber at the beginning to prepare the temporary script replacements, and a call to :quitGetLineNumber at the end to delete the temp files. I use FINDSTR coupled with :strlen to selectively get the required offset positions. The original script MUST use \r\n line terminators :!:

I believe the code below should work with scripts up to 999,9999 lines in length. Unlike your original, I can even get the line number after the :getLineNumber routines, (but not within them). The only added restriction is :getLineNumber should not be iterated multiple times within a FOR loop, (but it could be called prior to entry of a FOR loop).

getLineNumber.bat

Code: Select all

@echo off
call :initGetLineNumber

call :getLineNumber result
echo %result%

call :getLineNumber result
echo %result%

call :getLineNumber result&for %%G in ( 1 2 3) do echo OK %%G
echo %result%

call :test

::call :quitGetLineNumber
exit /b


:initGetLineNumber
setlocal enableDelayedExpansion
set "sp=="
for /l %%N in (1 1 13) do set "sp=!sp:~0,4000!!sp:~0,4000!"
setlocal disableDelayedExpansion
copy "%~f0" "%~f0.save" >nul
set /a off1=off2=0
8>"%~f0.mod1" 9>"%~f0.mod2" (
  for /f "delims=" %%A in ('findstr /ironc:"\<call  *:getLineNumber " "%~f0"') do (
    set "ln=%%A"
    for /f "tokens=1,2 delims=:" %%B in ("%%A") do (
      set "ln=%%A"
      setlocal enableDelayedExpansion
      set "ln=!ln:*:=!"
      set "ln=!ln:*:=!"
      set "label=%%B"
      call :strlen ln lnLen
      call :strlen label labelLen
      set /a "fill=%%C+lnLen-off1, fillCnt=fill/8002, fill=fill%%8002, off1=%%C+lnLen+labelLen+10"
      >&8 (
        for /l %%N in (1 1 !fillCnt!) do (echo !sp!)
        for %%N in (!fill!) do (echo(!sp:~-%%N!)
        (echo %%#:?=%%B%%)
      )
      set /a "fill=%%C+lnLen-labelLen-8-off2, fillCnt=fill/8002, fill=fill%%8002, off2=%%C+lnLen+2"
      >&9 (
        for /l %%N in (1 1 !fillCnt!) do (echo !sp!)
        for %%N in (!fill!) do (echo(!sp:~-%%N!)
        (echo :%%B)
        (echo %%#%%)
      )
      for /f "delims=" %%D in ("set /a off1=!off1!, off2=!off2!") do endlocal&%%D
    )
  )
)
exit /b


:getLineNumber  RtnVar
set ^"#=move /y "%~f0" "%~f0.mod1" ^>nul^&move /y "%~f0.mod2" "%~f0" ^>nul^&set ^^^"#=move /y "%~f0"^
 "%~f0.mod2"^^^>nul^^^&move /y "%~f0.save" "%~f0"^^^>nul^^^&set "%1=?"^^^&set #=^^^"^&goto :?^"
move /y "%~f0" "%~f0.save" >nul & move /y "%~f0.mod1" "%~f0" >nul & exit /b


:quitGetLineNumber
setlocal disableDelayedExpansion
del "%~f0.mod1" "%~f0.mod2" "%~f0.save"
exit /b


:strlen  StrVar  [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
  if "!s:~%%N,1!" neq "" (
    set /a "len+=%%N"
    set "s=!s:~%%N!"
  )
)
endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b


:test
call :getLineNumber result
echo %result%

And here are the temporary replacement scripts that are generated from the above code:

getLineNumber.bat.mod1

Code: Select all

================================================================
%#:?=4%
==================================
%#:?=7%
=====================================================================
%#:?=10%
========================... total line length = 2040 ...==============================
%#:?=83%

getLineNumber.bat.mod2

Code: Select all

=======================================================
:4
%#%
==================================
:7
%#%
====================================================================
:10
%#%
========================... total line length = 2040 ...==============================
:83
%#%


-----------------------------------------------------------------------------------------------------------

And now for that JREPL.BAT solution

This really simple, fast, reliable solution utilizes a prepareLineNumbers.bat script that must be called at the top of the script, before any line numbers are requested. The utility only modifies the script if it detects a change since the last time it was run. Besides embedding the line numbers, it also defines a %#% macro to set a ln# variable to the current line number.

prepareLineNumbers.bat

Code: Select all

::prepareLineNumbers  fullFilePath
call jrepl "(\x25#\x25)\d*" "$1+ln" /j /f "%~1" /o "%~1.new"
fc /b "%~1" "%~1.new" >nul && del "%~1.new" || move /y "%~1.new" "%~1" >nul
set "#=set /a ln#="


example.bat

Code: Select all

@echo off
setlocal enableDelayedExpansion
call prepareLineNumbers "%~f0"

%#%
echo !ln#!

%#%
echo !ln#!

for %%G in ( 1 2 3) DO (
   %#%&echo !ln#!
   echo OK
   %#%&echo !ln#!
)

exit /b

When the script is run for the first time, it is modified to become:

Code: Select all

@echo off
setlocal enableDelayedExpansion
call prepareLineNumbers "%~f0"

%#%5
echo !ln#!

%#%8
echo !ln#!

for %%G in ( 1 2 3) DO (
   %#%12&echo !ln#!
   echo OK
   %#%14&echo !ln#!
)

exit /b

The script will remain untouched on successive runs, until prepareLineNumbers.bat detects a change, at which point it will refresh the line numbers.


Dave Benham

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to: Get the current line number

#7 Post by dbenham » 17 May 2015 10:39

I figured out how to reduce jeb's design to a single temporary replacement file :!: :D

I increased the length of the function name so that the technique will work with any batch script with as many as 999999 lines, even if the name of the line number variable is a single character.

retrieveLineNumber.bat

Code: Select all

@echo off
call :initRetrieveLineNumber

call :retrieveLineNumber @
call :retrieveLineNumber #
echo %@%
echo %#%

call :retrieveLineNumber #
echo %#%

call :retrieveLineNumber #&for %%G in ( 1 2 3) do echo OK %%G
echo %#%

call :test

  call :quitRetrieveLineNumber
exit /b


:initRetrieveLineNumber
setlocal enableDelayedExpansion
set ^"LF=^

^" The empty line above is critical - DO NOT REMOVE
set "sp=="
for /l %%N in (1 1 13) do set "sp=!sp:~0,4000!!sp:~0,4000!"
setlocal disableDelayedExpansion
copy "%~f0" "%~f0.save" >nul
set /a offset=0
>"%~f0.#" (
  for /f "delims=" %%A in ('findstr /ironc:"\<call  *:retrieveLineNumber " "%~f0"') do (
    set "ln=%%A"
    for /f "tokens=1,2 delims=:" %%B in ("%%A") do (
      set "ln=%%A"
      setlocal enableDelayedExpansion
      set "ln=!ln:*:=!"
      set "ln=!ln:*:=!"
      set "label=%%B"
      call :strlen ln lnLen
      call :strlen label labelLen
      set /a "fill=%%C+lnLen-labelLen-6-offset, fillCnt=fill/8002, fill=fill%%8002, offset=%%C+lnLen+labelLen+10"
      for /l %%N in (1 1 !fillCnt!) do (echo !sp!)
      for %%N in (!fill!) do (echo(!sp:~-%%N!)
      (echo :%%B!LF!%%#%%!LF!%%#:?=%%B%%)
      for %%O in (!offset!) do endlocal&set "offset=%%O"
    )
  )
)
exit /b


:retrieveLineNumber  RtnVar
set ^"#=set ^^^"#=move /y "%~f0" "%~f0.#"^^^>nul^^^&move /y "%~f0.0" "%~f0"^^^>nul^^^&set #=^^^&set %1=?^^^"^&goto :?^"
move /y "%~f0" "%~f0.0" >nul & move /y "%~f0.#" "%~f0" >nul & exit /b


:quitRetrieveLineNumber
setlocal disableDelayedExpansion
del "%~f0.0" "%~f0.#"
exit /b


:strlen  StrVar  [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
  if "!s:~%%N,1!" neq "" (
    set /a "len+=%%N"
    set "s=!s:~%%N!"
  )
)
endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b


:test
call :retrieveLineNumber #
echo %#%

And here is the temporary replacement script that is created:

retrieveLineNumber.bat.#

Code: Select all

==============================================================
:4
%#%
%#:?=4%
==========
:5
%#%
%#:?=5%
================================
:9
%#%
%#:?=9%
========================================================
:12
%#%
%#:?=12%
=================... total line length = 1704 ...=====================
:79
%#%
%#:?=79%


I still prefer the JREPL.BAT based prepareLineNumbers.bat solution.


Dave Benham

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: How to: Get the current line number

#8 Post by Aacini » 17 May 2015 13:53

It seems that there are several ways to get line numbers! :) The method below is equivalent to Dave's modification of the same file, but the modification code is placed in a subroutine:

Code: Select all

@echo off

call :SetLineNumbers

set lineNumber=0
echo %lineNumber%

set lineNumber=0
echo %lineNumber%

for %%G in ( 1 2 3) DO (
   set lineNumber=0
)
echo %lineNumber%

exit /b


:SetLineNumbers
setlocal EnableDelayedExpansion

set "anyChange="
set "equal=="
set lastLine=0
< "%~F0" (
   for /F "delims=:" %%a in ('findstr /N /I /C:"set lineNumber%equal%" "%~F0"') do (
      set /A lines=%%a-lastLine-1, lastLine=%%a
      for /L %%i in (1,1,!lines!) do (
         set "line="
         set /P "line="
         echo(!line!
      )
      set /P "line="
      for /F "tokens=2 delims==" %%b in ("!line!") do (
         if "%%b" neq "%%a" (
            set "line=!line:%%b=%%a!"
            set "anyChange=true"
         )
      )
      echo !line!
   )
   findstr "^"
) > temp.tmp
if defined anyChange (
   move /Y temp.tmp "%~F0" > NUL
   exit /B
) else (
   del temp.tmp
)
exit /B

Antonio

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to: Get the current line number

#9 Post by dbenham » 20 Jun 2015 18:06

I've improved my JREPL.BAT solution - it no longer uses any variable :!:

All it takes is a disappearing variable comment that functions as an opening and closing tag. JREPL locates the opening and closing tag and replaces the content with the line number :D

I use %#=% as an opening tag and %=#% as a closing tag.

I opted to embed the JREPL call directly at the top of the script.

Code: Select all

@echo off
::prepareLineNumbers
>nul 2>nul (
  call jrepl "(\x25#=\x25)\d*(\x25=#\x25)" "$1+ln+$2" /j /f "%~f0" /o "%~f0.new"
  fc /b "%~f0" "%~f0.new" && del "%~f0.new" || move /y "%~f0.new" "%~f0"
)


echo line %#=%%=#%

echo line %#=%%=#%


for %%G in ( 1 2 3) DO (
   echo Within loop iteration %%G: line %#=%%=#%
   echo Within loop iteration %%G: line %#=%%=#%
)

exit /b

And here is what the code looks like after it is run:

Code: Select all

@echo off
::prepareLineNumbers
>nul 2>nul (
  call jrepl "(\x25#=\x25)\d*(\x25=#\x25)" "$1+ln+$2" /j /f "%~f0" /o "%~f0.new"
  fc /b "%~f0" "%~f0.new" && del "%~f0.new" || move /y "%~f0.new" "%~f0"
)


echo line %#=%9%=#%

echo line %#=%11%=#%


for %%G in ( 1 2 3) DO (
   echo Within loop iteration %%G: line %#=%15%=#%
   echo Within loop iteration %%G: line %#=%16%=#%
)

exit /b

After the initial run, it will remain static on successive runs (last modified date not updated) unless the content is changed.


Dave Benham

Erik Bachmann
Posts: 12
Joined: 25 Feb 2015 00:47
Location: Odense, Denmark

Re: How to: Get the current line number

#10 Post by Erik Bachmann » 30 Jun 2023 08:40

This is ingenius!

But may I suggest a slight modification of the opening and closing tags:

Code: Select all

echo line %#=%%=#%
Neither can be expanded since they litteraly are illegal as variables due to the "=".

If the tags are modifyed slightly to say:

Code: Select all

echo %#-_%%_-#%

Then you can expand the tags like:

Code: Select all

	SET "#-_=Line [
	SET "_-#=] found"
	echo %#-_%%_-#%
This will produce an output like:

Code: Select all

Line [3] found
And makes it simple to translate:

Code: Select all

	SET "#-_=Ligne [
	SET "_-#=] trouvée

Post Reply