This might be an XY question, but I am trying to echo various lines from a text file.
@setlocal enableextensions enabledelayedexpansion
@echo off
set /a "line = 0"
for /f "tokens=* delims= " %%a in (file.txt) do (
set /a "line = line+1"
if !line!==18 set thing1=%%a
if !line!==19 set thing2=%%a
if !line!==20 set thing3=%%a
)
endlocal & set thing1=%thing1% & set thing2=%thing2% & set thing3=%thing3%
echo:
echo %thing1%
echo %thing2%
echo %thing3%
pause
This works well and is neat compared to others I found so I was tiring to to make it more adaptable. I can make the line numbers variable, but what if I wanted four lines, or a range of lines? So I tried to make a for loop. List all the lines:
(
echo @setlocal enableextensions enabledelayedexpansion
echo @echo off
echo set /a "line = 0"
echo for /f "tokens=* delims= " %%a in (file.txt^) do (
echo set /a "line = line+1"
)>>test.bat
for /l %%m in (1,1,10) do (
echo if !line!==%%m set thing%%m=%%%%a
)>>test.bat
echo )>>test.bat
echo endlocal ^& )>>test.bat
for /l %%m in (1,1,10) do (
echo set thing%%m^=%%thing%%%%m ^&
)>>test.bat
Then I could echo %thing(anynumber%)
as I wished.
This runs into problems when the for loop list need to be on the same line:
endlocal & set thing1=%thing1% & set thing2=%thing2% & set thing3=%thing3%
Instead it outputs:
endlocal &
set thing1=%thing%1 &
set thing2=%thing%2 &
set thing3=%thing%3 &
etc...
I know prompt $H
can backspace, but I think that is a dead end for what I am trying here. I can't find much on reverse line feed online. Also it adds an ampersand to the final %%thing%%
in the list.
Sample file.txt
This is line one
This is line two
This is line three
This is line four
This is line five
This is line six
This is line seven
This is line eight
This is line nine
This is line ten
Maybe findstr is the way to go about this. I found this and edited it to suit what I was trying to do:
:start
cls
set /p "line= Which lines?"
for /f "tokens=* delims=[] " %%a in ('type file.txt^|find /v /n ""') do (
echo/%%a|findstr /l /b "%line%" >nul && echo/%%a
)
pause
goto :start
But with an input of 1 it will echo every instance beginning with "1" i.e. 1,11,12,13 etc. This seems to be nearly there. I've tried various switches from the findstr /?
, but can't figure it out. If it could do input ranges too, so input line 1-5,7,12-15 would echo them 10 lines.
This should do (although not with ranges):
@echo off
setlocal
set lines=1,5,6,8
(for %%a in (%lines%) do echo %%a:)>lines.txt
for /f "tokens=1,* delims=:" %%a in ('type file.txt^|findstr /n "^"^|findstr /bg:lines.txt') do echo/%%b
del lines.txt
The first for
loop builds a temporary file for findstr /g
(see findstr /?
for details).
The second one adds line numbers, looks them up in the file and prints the original line if the line number is in the file.
The numbers in %lines%
can be delimited by any standard delimiters (TABs, SPACEs, Commas and even =
(not recommended - stay with spaces and/or commas)
To expand to Ranges 5-10
, you'd need to parse %lines%
with some more code (hard to make that fool-proof) and "translate" to single line numbers to write into lines.txt
(We could also expand %lines%
to a REGEX search string or findstr
, avoiding a temporary file, but it's much easier to understand and maintain this way)
Edit: implemented a simple "range extension" (without checking for plausability):
@echo off
setlocal
set "lines=1,4-6,9"
(for %%a in (%lines%) do (
echo %%a|find "-" >nul && call :range %%a || echo/%%a:
))>lines.txt
echo lines %lines% are:
for /f "tokens=1,* delims=:" %%a in ('type file.txt^|findstr /n "^"^|findstr /bg:lines.txt') do echo/%%b
del lines.txt
goto :eof
:range
for /f "tokens=1,2 delims=-" %%b in ("%1") do (
for /l %%i in (%%b,1,%%c) do echo %%i:
)
goto :eof
Output (with your sample input file):
lines 1,4-6,9 are:
This is line one
This is line four
This is line five
This is line six
This is line nine
PS: this outputs the lines in their original order (set "lines=1,4-6,8"
and set "lines=8 1 4-6"
give the same output (due to how findstr /g
works))