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>
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)?
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