Search code examples
windowsbatch-filecmdfindstr

Extracting specific lines (based on the line number) using batch file


I have a need to extract specific lines from a text file (input.txt). I have already found a solution but have some clarifications why it is not working when used with setlocalenabledealyed expansion. Below is the example

My input.txt (numbered using findstr /n). In this example, I need to extract the lines between line number 27 to 39. In my full batch file, these values are called from another module.

1:[root]
2:ASKEHPPTEQWEIRAZOKXL
...
...
...
27:[parent2/child2]
28:NJ35CG5D9DEYXKMQKBJX
29:ZAZRMGB2E0KAG85FSPI3
30:E9046AQL44LV1R79OT8E
31:8OPXV1QYCTQVK34JZ2KV
32:3E32PWHGX5RGTFUXI9GC
33:H7DTDDFQZVGETGL764YU
34:174UOQMW35BCIQJNR1P8
35:7B3V0E9QXFQOM3NF08CZ
36:QH6FZVMKKGHKF0J8PB5O
37:QCRC90QCWWGAHRWBVMUI
38:4QPVEJW75GFW8DUM1PGU
39:[parent2/child3]
...
...
...
3000:[end]

1st code - calling.bat - using CALL

@echo OFF

set st_ln=27
set end_ln=39

for /f "tokens=1,2 delims=:" %%a in (input.txt) do call :extract "%%a" "%%b"
goto :eof

:extract
   set "ln=%~1"
   set "sid=%~2"
   if %ln% LEQ %st_ln% goto :eof
   if %ln% GEQ %end_ln% goto :eof
   echo.%sid%
   goto :eof

:eof

2nd code - local.bat used with setlocalenabledelayedexpansion

REM @echo OFF

set st_ln=27
set end_ln=39

SETLOCAL ENABLEDELAYEDEXPANSION

for /f "tokens=1,2 delims=:" %%a in (input.txt) do (
   set "ln=%%a"
   set "sid=%%b"
   if !ln! LEQ !st_ln! goto :eof
   if !ln! GEQ !end_ln! goto :eof
   echo.!sid!
)

:eof

OUTPUT - Calling.bat works absolutely fine. But local.bat does not return any error, but it iterates the for loop only once. (I tried replacing !st_ln! & !end_ln! with %st_ln% & %end_ln%, but still the same issue) Why? What am I missing?

C:\Temp>calling.bat
NJ35CG5D9DEYXKMQKBJX
ZAZRMGB2E0KAG85FSPI3
E9046AQL44LV1R79OT8E
8OPXV1QYCTQVK34JZ2KV
3E32PWHGX5RGTFUXI9GC
H7DTDDFQZVGETGL764YU
174UOQMW35BCIQJNR1P8
7B3V0E9QXFQOM3NF08CZ
QH6FZVMKKGHKF0J8PB5O
QCRC90QCWWGAHRWBVMUI
4QPVEJW75GFW8DUM1PGU

C:\Temp>local.bat

C:\Temp>REM @echo OFF

C:\Temp>set st_ln=27

C:\Temp>set end_ln=39

C:\Temp>SETLOCAL ENABLEDELAYEDEXPANSION

C:\Temp>for /F "tokens=1,2 delims=:" %a in (input.txt) do (
set "ln=%a"
 set "sid=%b"
 if !ln! LEQ !st_ln! goto :eof
 if !ln! GEQ !end_ln! goto :eof
 echo.!sid!
)

C:\Temp>(
set "ln=1"
 set "sid=[root]"
 if !ln! LEQ !st_ln! goto :eof
 if !ln! GEQ !end_ln! goto :eof
 echo.!sid!
)

C:\Temp>

Supplementary Question :

As I said above, I could get the expected result, but just want to know if it can be enhanced. I want to extract specific lines (lines could be anywhere in input file and number of line I need could be 1 to 20), from the input file which could have up to 3000 lines. Currently in the above code I am using the for loop and checking the line number for each line and so it will iterate through the for loop for 3000 times, which could potentially delay the output. Is there any other way to avoid this and to speed up the script (may be findstr 'range' search)?


Solution

  • Your local.bat does not work because when the first line is readed, with number 1, and starting line 27, the condition if !ln! LEQ !st_ln! goto :eof is evaluated as true and the goto command executed, leaving the for loop.

    For the supplementary question, you can calculate how many rows need to be skipped at the start of the file and then only process until the end line is reached. As no variable is changed inside the loop, there is no need for delayed expansion

    set /a "skipLines=st_ln-1"
    if %skipLines% gtr 0 ( 
        set "skipLines=skip=%skipLines%" 
    ) else ( 
        set "skipLines=" 
    )
    
    
    for /f "%skipLines% tokens=1,2 delims=:" %%a in (input.txt
    ) do if %%a gtr %end_ln% ( goto :eof ) else ( 
        echo(%%b
    )
    

    The if %skiplines% gtr 0 test is necessary as the for /f loop does not allow a skip=0 clause