Extract a Range of Lines from a Text File
Below is a little Batch program to extract a range of lines from a text file and output them to the screen. A simple enough task, or so I thought!
And if you are a Batch newbie, the best way to learn is as you go along. The simplest way to get help “on the fly” is to enter
help | more /e from the command line which will spit out a list of commands that have built-in help.
For instance, if you want to know more about argument references (%0, %1, etc), type
call /?. For some commands, the built-in help must be piped through
findstr /? | more /e, and ironically:
more /? | more /e).
First, a big thank you to Batch-guru Jeb for sharing his wisdom about
!line:*:=! technique, and how to display characters like the exclamation mark even when delayed expansion is switched on.
Here is my bare-bones version. I named it
slice.cmd, but feel free to call it whatever you like.
@echo off & setlocal enableextensions set /a begin=%1-1,end=%2 if %begin% gtr 0 (set "skip=skip=%begin% ") else set skip= for /f "%skip%delims=" %%a in ('findstr /n "^" "%~3"') do ( set "line=%%a" set /a begin+=1 setlocal enabledelayedexpansion if !begin! gtr %end% goto end set "line=!line:*:=!" echo(!line! endlocal ) :end endlocal & exit /b 0
The problem is more complicated than you might think at first glance. The
for /f loop suppresses blank lines and lines beginning with the badly-named “end-of-line” comment character (semi-colon by default). And there's the problem of
echo outputting the infamous “poison characters” (! ` ^ & | < > ( ) = ~ etc).
The program gets around these problems by using
/n switch to generate numbered output lines (line number followed by a colon). This tricks
for /f into passing every line in the specified range—even blank lines or lines beginning with a semi-colon—to the body of the loop.
But now every line has an unwanted number and colon stuck to the front of it. To get rid of them, the program copies the value stored in the loop variable (
%%a) into another variable called
line. Only then is it safe to turn on delayed expansion. Next,
set "line=!line:*:=!" truncates the line to the first colon found. And lastly,
echo(!line! outputs the line to the screen.
By the way, you might be wondering why all the smoke and mirrors around the
for /f loop's
skip option? It's to avoid
skip=0 which causes an error.
No checking is done on the parameters whatsoever! The first and second arguments should be unsigned integers. The first argument should be less than or equal to the second. The final argument is a filename. No test is done to check if it's a folder or a file, or if it's empty, or if it even exists. And, as per usual, no usage info. These tasks are left as exercises to be completed by the reader 😉
Seriously, these are all topics for future posts. Subscribe to the RSS Posts feed or sign up for email notifications (see footer) to be kept up to date.
If you need a more substantive software solution, try
tail which are included in the highly-recommended CoreUtils for Windows package. You can do cool stuff like
head -50 file.txt | tail -20 which will output the 31st to 50th lines of