For the task at hand, npocmaka's answer is the best suitable approach, as it does not insist on a fixed position of the string to extract from the file.
However, I want to provide a variant that sticks to a certain position.
The following code extracts the string placed at columns 21 to 50 in line 4 of file list.txt
(the result is echoed (enclosed in ""
) and stored in variable LINE_TXT
(without ""
):
@echo off
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" "list.txt"'
) do (
if %%L equ 4 (
set "LINE_TXT=%%M"
goto :NEXT
)
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
The goto :NEXT
command terminates the for /F
loop at the given line; this is not mandatory but will improve performance for huge files (as long as the given line number is quite small).
To be more flexible, the following code can be used (define the string position in the initial set
block):
@echo off
rem Define the string position here:
set FILE_TXT="list.txt"
set LINE_NUM=4
set COL_FROM=21
set COL_UPTO=50
setlocal EnableDelayedExpansion
set /A COL_UPTO-=COL_FROM
set /A COL_FROM-=1
for /F "tokens=1,* delims=:" %%L in (
'findstr /N /R ".*" %FILE_TXT%'
) do (
if %%L equ %LINE_NUM% (
set "LINE_TXT=%%M"
if defined LINE_TXT (
set "LINE_TXT=!LINE_TXT:~%COL_FROM%,%COL_UPTO%!"
)
goto :NEXT
)
)
:NEXT
endlocal & set "LINE_TXT=%LINE_TXT%"
echo."%LINE_TXT%"
Both of the above code snippets rely on the output of findstr /N /R ".*"
, which returns every line that matches the regular expression .*
, meaning zero or more characters, which in turn is actually true for every line in the file; however, the switch /N
defines to prefix each line with its line number, which I extract and compare with the originally defined one.
Here is another variant which uses for /F
to directly loop through the content (lines) of the given text file, without using findstr
:
@echo off
for /F "usebackq skip=3 eol== delims=" %%L in (
"list.txt"
) do (
set "LINE_TXT=%%L"
goto :NEXT
)
:NEXT
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
This method has got the better performance, because there is the skip
option which skips parsing of and iterating through all lines (1 to 3) before the line of interest (4), opposed to the findstring
variant.
However, there is one disadvantage:
for /F
features an eol
option which defines a character interpreted as line comment (and defaults to ;
); there is no way to switch this option off as long as delims=
defines no delimiters (last position in option string), which is mandatory here to return the line as is; so you have to find a character that does not appear as the first one in any line (I defined =
here because your sample text file uses this as header/footer character only).
To extract a string from line 1, remove the skip
option as skip=0
results in a syntax error.
Note that goto :NEXT
is required here; otherwise, the last (non-empty) line of the file is extracted.
Although for /F
does not iterate any empty lines in the file, this is no problem here as the skip
option does not check the line content and skip over empty lines as well.
Finally, here is one more approach using more +3
where no text parsing is done. However, a temporary file is needed here to pass the text of the desired line to the variable LINE_TXT
:
@echo off
set LINE_TXT=
more +3 "list.txt" > "list.tmp"
set /P LINE_TXT= < "list.tmp"
del /Q "list.tmp"
if defined LINE_TXT set "LINE_TXT=%LINE_TXT:~20,29%"
echo."%LINE_TXT%"
exit /B 0
This method avoids for /F
and therefore the problem with the unwanted eol
option as mentioned in the above solution. But this does not handle tabs correctly as more
substitutes them with spaces (8 indent spaces as per default and configurable by the /Tn
switch where n
is the number of spaces).