Unexpected results while replacing single characters in a text file - string

My batch file:
#ECHO off
(FOR /f "delims=" %%i in (source.txt) DO (
SET "line=%%i"
setlocal enabledelayedexpansion
SET "line=!line:Ć=F!"
SET "line=!line:Ç=G!"
SET "line=!line:Ň=R!"
SET "line=!line:Ô=T!"
ECHO.!line!
endlocal
))>"output.txt"
My source.txt file:
ĆÇŇÔ
Expected output.txt file:
FGRT
Current output.txt file:
FFRR
My question is: what's wrong here?

If source.txt is not saved as Unicode, your issue may be related to the codepage at the time you run your loop.
The following example switches to codepage 1252, West European Latin, (as also suggested in the comments by Gerhard), if not that already. Although I'd assume codepage 850, Multilingual (Latin I) should work equally well. (Just change to the codepage required by replacing 1252 on lines 7, and 8, as necessary).
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
If Not Exist "source.txt" GoTo :EOF
For /F "Delims==" %%G In ('2^> NUL Set _cp') Do Set "%%G="
For /F Tokens^=* %%G In ('"%SystemRoot%\System32\chcp.com"'
) Do For %%H In (%%G) Do Set "_cp=%%~nH"
If Not %_cp% Equ 1252 (Set "_cpc=TRUE"
"%SystemRoot%\System32\chcp.com" 1252 1> NUL)
(For /F UseBackQ^ Delims^=^ EOL^= %%G In ("source.txt") Do (
Set "line=%%G"
SetLocal EnableDelayedExpansion
Set "line=!line:Ć=F!"
Set "line=!line:Ç=G!"
Set "line=!line:Ň=R!"
Set "line=!line:Ô=T!"
Echo=!line!
EndLocal)) 1> "output.txt"
If Defined _cpc "%SystemRoot%\System32\chcp.com" %_cp% 1> NUL
Please note that using a For loop like this, will remove any blank lines from the output

The answer (as suggested by #Gerhard and #Compo): it was the wrong code page.
Below is my current working batch code if someone else will ever be in the same need (to convert inverted ATASCII characters in ATARI BASIC code).
It converts defined set of characters (you can add more / delete some - just modify strings and the total number of characters) and make comments more visible by adding lines at the start and at the end of each one.
#ECHO off
rem --------------------------------------------------
rem CHECK FOR THE SOURCE FILE
rem --------------------------------------------------
IF "%~1"=="" GOTO :End
rem --------------------------------------------------
rem SET THE CODE PAGE
rem --------------------------------------------------
CHCP 1252 > NUL
rem --------------------------------------------------
rem DEFINE THE SET OF CHARACTERS TO CONVERT
rem --------------------------------------------------
SET "input_set_of_chars= Ł¤§¨cŞ«¬­R°+˛ł´u¸ą»Ľ˝ľÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚř"
SET "output_set_of_chars= #$'()*+,-.0123456789;<=>ABCDEFGHIJKLMNOPQRSTUVWXYZx"
SET "number_of_chars=52"
rem --------------------------------------------------
rem CONVERT EACH LINE OF THE SOURCE FILE
rem --------------------------------------------------
(FOR /f "delims=" %%i in (%~1) DO (
SET "line=%%i"
CALL :ConvertASCII
))> "%~n1-converted%~x1"
GOTO :End
rem --------------------------------------------------
rem START OF THE CONVERT SUBROUTINE
rem --------------------------------------------------
:ConvertASCII
SETLOCAL enableDelayedExpansion
rem --------------------------------------------------
rem IF THE LINE IS NOT A COMMENT THEN DO NOT CONVERT
rem --------------------------------------------------
IF "!line!"=="!line: REM =!" GOTO :LoopEnd
rem --------------------------------------------------
rem MAKE COMMENT LINE A LITTLE MORE VISIBLE
rem --------------------------------------------------
SET "line=!line: REM = REM ----------!----------"
rem --------------------------------------------------
rem CONVERT ALL DEFINED CHARACTERS
rem --------------------------------------------------
SET "counter=0"
:LoopStart
SET "input_char=!input_set_of_chars:~%counter%,1!"
SET "output_char=!output_set_of_chars:~%counter%,1!"
SET "line=!line:%input_char%=%output_char%!"
SET /a counter+=1
IF "!counter!"=="!number_of_chars!" GOTO :LoopEnd
GOTO :LoopStart
rem --------------------------------------------------
rem ECHO THE CURRENT LINE AND EXIT SUBROUTINE
rem --------------------------------------------------
:LoopEnd
ECHO.!line!
ENDLOCAL
EXIT /b 0
:End

As per your comment, it is actually not really a code page issue, because you have got ATASCII encoding in your *.bas files. For converting such files to avoid inverted glyphs, I would use a language that can easily read the file in binary mode and subtract 0x80 from every byte whose value is greater than or equal to 0x80.
Anyway, if you do want to replace the characters left over from your already performed conversion process (Ć, Ç, Ň, Ô, with codes 0x8F, 0x80, 0xD5, 0xE2, resp., as per your active code page 852), I would do it the following way, applying code page 437 during any conversion activities, because this defines the character set of the original IBM PC, also known as OEM font, where there should not occur any unwanted character conversion in the background:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (full path to target directory)
set "_SOURCE=source.txt" & rem // (name of source file)
set "_RETURN=return.txt" & rem // (name of return file)
set "_FILTER=^[0-9][0-9]* *REM " & rem /* (`findstr` search expression to filter
rem for specific lines; `^` means all) */
rem // Store current code page:
for /F "tokens=2 delims=:" %%P in ('chcp') do for /F %%C in ("%%P") do set "$CP=%%C"
rem // Set code page to OEM in order to avoid unwanted character conversions:
> nul chcp 437
rem /* Specify character replacements; the `forfiles` command supports substitution
rem of hex codes like `0xHH`, so you can specify special characters by their code
rem in order to avoid having to embed them into this script, which might in turn
rem lead to problems due to dependencies on the current code page; each `0x22`
rem represents a `"` to enclose each replacement expression within quotes; each
rem of the following replacement expression is `"` + char. + `=` + char. +`"`: */
for /F "delims=" %%R in ('
forfiles /P "%~dp0." /M "%~nx0" /C ^
"cmd /C echo 0x220x8F=F0x22 0x220x80=G0x22 0x220xD5=R0x22 0x220xE2=T0x22"
') do set "RPL=%%R"
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Write into return file:
> "%_RETURN%" (
rem /* Read from source file, temporarily precede each line with line number
rem followed by a `:` in order to not lose blank lines: */
for /F "delims=" %%L in ('findstr /N "^" "%_SOURCE%"') do (
rem // Store current line:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Remove temporary line number prefix:
set "LINE=!LINE:*:=!"
rem // Filter for lines that are subject to the replacements:
cmd /V /C echo(^^!LINE^^!| > nul findstr /R /I /C:"!_FILTER!" && (
rem // Perform replacements one after another:
for %%R in (%RPL%) do if defined LINE set "LINE=!LINE:%%~R!"
)
rem // Return resulting line:
echo(!LINE!
endlocal
)
)
rem // Return from target directory:
popd
)
rem // Restore former code page:
if defined $CP > nul chcp %$CP%
endlocal
exit /B
This approach performs the character replacements only in lines that begin with: a decimal number, followed by one or more SPACEs, followed by REM in a case-insensitive manner, followed by a SPACE.
Here is now a script that truly converts ATASCII characters in REM comments in your Atari Basic (*.bas) file, using certutil for converting the binary character codes:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_TARGET=%~dp0." & rem // (full path to target directory)
set "_SOURCE=source.txt" & rem // (name of source file)
set "_RETURN=return.txt" & rem // (name of return file)
set "_FILTER=^[0-9][0-9]* *REM " & rem /* (`findstr` search expression to filter
rem for specific lines; `^` means all) */
set "_TEMPFN=%TEMP%\%~n0_%RANDOM%" & rem // (path and base name of temporary file)
rem // Change into target directory:
pushd "%_TARGET%" && (
rem // Write into return file:
> "%_RETURN%" (
rem /* Read from source file, temporarily precede each line with line number
rem followed by a `:` in order to not lose blank lines: */
for /F "delims=" %%L in ('findstr /N "^" "%_SOURCE%"') do (
rem // Store current line:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Remove temporary line number prefix:
set "LINE=!LINE:*:=!"
rem // Filter for lines that are subject to the replacements:
cmd /V /C echo(^^!LINE^^!| > nul findstr /R /I /C:"!_FILTER!" && (
rem // Found a line, hence write it to temporary file:
(> "!_TEMPFN!.tmp" echo(!LINE!) && (
rem // Convert temporary file to hex dump file:
> nul certutil -f -encodehex "!_TEMPFN!.tmp" "!_TEMPFN!.cnv" 4 && (
rem // Write to temporary file:
(> "!_TEMPFN!.tmp" (
rem // Read hex dump file line by line:
for /F "usebackq tokens=*" %%T in ("!_TEMPFN!.cnv") do (
rem // Reset buffer, loop through hex values:
set "BUFF= " & for %%H in (%%T) do (
rem // Determine new hex value, append it to buffer:
set "HEX=%%H" & set /A "FIG=0x!HEX:~,1!-0x8"
if !FIG! lss 0 (
rem // Value was < 0x80, hence keep it:
set "BUFF=!BUFF! !HEX!"
) else (
rem // Value was >= 0x80, hence subtract 0x80:
set "BUFF=!BUFF! !FIG!!HEX:~1!"
)
)
echo(!BUFF:~2!
)
)) && (
> nul certutil -f -decodehex "!_TEMPFN!.tmp" "!_TEMPFN!.cnv" 4 && (
type "!_TEMPFN!.cnv"
) || echo(!LINE!
) || echo(!LINE!
) || echo(!LINE!
) || echo(!LINE!
) || (
rem // Return resulting line:
echo(!LINE!
)
endlocal
)
)
rem // Clean up temporary files:
del "%_TEMPFN%.tmp" "%_TEMPFN%.cnv"
rem // Return from target directory:
popd
)
endlocal
exit /B

Related

Replace a string, in some conditions, in a file

I have file whose format like this:
tcpport 1500
webports 1500,1500
tcpclientport 1500
I want to replace 1500 by 1510, only
when I find the string webports.
I found many scripts on searching and replacing strings, but they replace all the occurrences of a string by another string. That's not what I want.
How can I do that in a Windows batch file?
Here is an answer
================================
#::-----------------------------------------------------------------------------------
#:: --- Fonction substitute_str
#::-----------------------------------------------------------------------------------
:substitute_str
#echo off
setlocal enableextensions disabledelayedexpansion
set "oldstring=%1" rem string to replace
set "newstring=%2" rem replacing string
set "textFile=%3" rem file where replacement will take place
set "texttosearch=%4" rem string to be present in the line so that replacement could take place.
set "FICWORK=C:\temp\EvoSvgWin.wrk"
set "FICTEMP=C:\temp\EvoSvgWin.out"
for /f "delims=" %%i in ('type %textFile% ^& break ^> %textFile% ') do (
set "line=%%i"
type NUL > %FICTEMP%
echo(!line! > %FICWORK%
type "%FICWORK%" | findstr /I "%texttosearch%" > %FICTEMP%
for %%S in ("%FICTEMP%") do ( #set "size=%%~zS" )
if NOT !size! EQU 0 (
echo(!line:%oldstring%=%newstring%! >> %textFile%
) else (
echo(!line! >> %textFile%
)
endlocal
)
del %FICTEMP% %FICWORK%
EXIT /B 0
==========================================================================
NB : >> %textFile% is necessary because the function can be called many times with the same file so that all replacements could take place in the same.

Batch File Help: Sorting files into specifically-named folders based on filename with exceptions

My operating system is Windows 7 and I have files with names such as:
123.txt
abcd_123.txt
abcd_1234.txt
bcde_123_456.txt
bcde_123_4567.txt
cde_fgh_123_456.txt
cde_fgh_123_4567.txt
I would like for folders to be generated based on the starting parts of these filenames (or without the trailing numbers) and prefaced with a specific character, and for the files to then be sorted accordingly into them. Example result:
#abcd\abcd_123.txt
#abcd\abcd_1234.txt
#bcde\bcde_123_456.txt
#bcde\bcde_123_4567.txt
#cde_fgh\cde_fgh_123_456.txt
#cde_fgh\cde_fgh_123_4567.txt
*123.txt is skipped / not sorted.
This is the code I've come up with so far:
#echo OFF
setlocal enabledelayedexpansion
set var_dir="#Sorted"
for /f "delims=_" %%i in ('dir /b /a-d *_*.txt') do (
mkdir "#Sorted\#%%i" 2>nul
move "%%i_*.txt" "%var_dir%\#%%i" >NUL 2>nul
)
echo Sorting Complete!
#pause
GOTO :EOF
It works, but I am not sure how to:
Ignore filenames that start with a number (0-9).
Obtain strings beyond the first delim (_).
As for the second point, I think the filenames can sometimes be too complex to correctly differentiate which part to use as the name for the folder. Example, it sorts:
cde_fgh_123_4567.txt
Into:
#cde\cde_fgh_123_4567.txt
As such, I was thinking for the algorithm to be something like:
Set Folder Name to
(1) string before (first) "_" if string is greater than 3 characters
OR
(2) entire string before second "_" if first string is less than or equal to 3 characters
Thus, the example above should be changed to:
#cde_fgh\cde_fgh_123_4567.txt
How do I improve my batch code to obtain the desired outcome?
Your specifications are not clear enough. I assumed that the folder name is comprised of the first parts of filename that are not numbers. This method works with your filename examples:
#echo off
setlocal EnableDelayedExpansion
for %%a in (*.txt) do (
call :getFolder "%%~Na"
if defined folder (
if not exist "!folder!\" mkdir "!folder!"
move "%%a" "!folder!"
)
)
goto :EOF
:getFolder filename
set "file=%~1"
rem Separate filename in parts at undescores and
rem collect them while part is not a number (greater than zero)
set "folder="
set "part=%file:_=" & set /A "num=part" & (if !num! equ 0 set "folder=!folder!_!part!") & set "part=%"
if defined folder set "folder=#%folder:~1%"
exit /B
For example, this is the result obtained with your data:
File "123.txt" folder ""
File "abcd_123.txt" folder "#abcd"
File "abcd_1234.txt" folder "#abcd"
File "bcde_123_456.txt" folder "#bcde"
File "bcde_123_4567.txt" folder "#bcde"
File "cde_fgh_123_456.txt" folder "#cde_fgh"
File "cde_fgh_123_4567.txt" folder "#cde_fgh"
You could do the following:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=D:\#Sorted"
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Loop through matching files:
for /F "eol=| tokens=1,2,* delims=_" %%A in ('dir /B /A:-D-H-S "*_*.txt"') do (
rem // Skip if file name consists of less than two `_`-separated parts:
if not "%%B"=="" (
rem // Check if file name consists of more than two parts:
if "%%C"=="" (
rem // Two parts, so part one becomes directory name:
2> nul md "#%%A"
move "%%A_%%B" "#%%A\%%A_%%B"
) else (
rem // More than two parts:
set "ONE=%%A"
setlocal EnableDelayedExpansion
rem // Check length of part one:
if "!ONE:~3!"=="" (
rem /* Part one contains not more than 3 characters,
rem hence parts one and two become directory name: */
endlocal
2> nul md "#%%A_%%B"
move "%%A_%%B_%%C" "#%%A_%%B\%%A_%%B_%%C"
) else (
rem /* Part one contains more than 3 characters,
rem hence only part one becomes directory name: */
endlocal
2> nul md "#%%A"
move "%%A_%%B_%%C" "#%%A\%%A_%%B_%%C"
)
)
)
)
rem // Return from target directory:
popd
)
endlocal
exit /B
You could quickly do this using this code:
#(SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET "_SrcFolder=C:\Admin\CMD\s-e\tmp"
REM Use for DIR to pre-filter the list as much as possible
SET "_FileGlob=*_*.txt"
REM Used for FindStr Matches a Value that Begins with non numeric characters, followed by an underscore multiple times, followed by any number of numeric characters and underscore and ending in .txt
SET "_FileRegex=!_SrcFolder:\=\\!\\[a-Z][a-Z_]*_[0-9][0-9_]*.txt$"
)
CALL :Main
( ENDLOCAL
EXIT /B
)
:Main
REM Loop through the file sin the directory filtering non-matches and then perform actions based on matches
ECHO."%_FileRegex%"
FOR /F "Tokens=*" %%A IN ('
DIR /S/B/A-D "%_SrcFolder%\%_FileGlob%" ^|
FINDSTR /r "%_FileRegex%"
') DO (
SET "_FileName=%%~nA"
REM Break File Name after the Characters needed for the directory
FOR /F "TOKENS=* delims=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" %%a IN (
"%%~nA"
) DO (
REM Create Folder
IF NOT EXIST "%_SrcFolder%\#!_FileName:_%%a=!\" (
ECHO.&ECHO.== Creating Folder "#!_FileName:_%%a=!"
MD "%_SrcFolder%\#!_FileName:_%%a=!\"
)
REM Move Original File to the New Directory
ECHO. + Moving "%%~nxA" TO "#!_FileName:_%%a=!"
MOVE /Y "%%A" "%_SrcFolder%\#!_FileName:_%%a=!\%%~nxA" >NUL
)
)
GOTO :EOF

replace unneeded spaces with commas in txt using batch

I have a text file that contains some information and looks like this:
7253 198760.294 533963.581
7373 198752.213 533954.046
739CT 198751.288 533952.902
In every line there are parameters that has spaces between them but the spaces (not tabs) are for ease or reading only.
I need it to look like this:
1550,168040.682,630305.751,
1575,168023.241,630287.837,
15964TS,168008.317,630272.508,
Here is my code:
#echo off
setlocal EnableDelayedExpansion
set LF=^
set "content="
for /f "delims=" %%x in (input.txt) do (
set "content=!content!%%x!LF!"
)
:loop
if defined content (
set "new=!content: = !"
if "!new!" neq "!content!" (
set "content=!new!"
goto :loop
)
)
if defined content if "!str:~0,1!" equ " " set "content=!str:~1!"
if defined content if "!str:~-1!" equ " " set "content=!str:~0,-1!"
echo(!content!
set string=!content! & echo !string: =,! > output.txt
endlocal
pause > null
It turns everything in one line and connects between everything without spaces.
Concatenation to a single string is dangerous, because of limited max string length. Better process each line on it's own:
(for /f "tokens=1-3" %%a in (infile.txt) do (
echo %%a,%%b,%%c,
))>outfile.txt
note: empty lines will be ignored (will get lost)

Append a string in a file after another string through a windows batch file

I'm trying to write a batch file to append a certain string "str2 => bbb" to a file if it is not yet present in the file. "str2" will go after the string "str1 => aaa" (that always exists in the file). For example:
file.txt
...
str1 => aaa
...
end of file.txt
it will become:
file.txt
...
...
...
str1 => aaa
str2 => bbb
...
end of file.txt
and the batch file must be not destructive, i.e. if "str2" already exists in the file, the batch will do nothing.
I know how to find a string in the file:
FINDSTR "str2 => bbb" "file.txt"
IF %errorlevel%==0 (
ECHO FOUND
)
but I don't know what else to do to write the other string in the next line.
Since it is not clear to me whether str2 must occur immediately after str1 in the file or just anywhere, I wrote the following script which is capable of covering both criterias. It directly modifies the input file in case, so be careful. The input file must be speficied as a command line argument:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "FILE=%~1" & rem // (input file; `%~1` takes first command line argument)
set "WORD1=str1" & rem // (word after which `%WORD2%` must be inserted)
set "WORD2=str2" & rem // (word that must be present in the file)
set "STRING2=%WORD2% => bbb" & rem // (full string to insert if `%WORD2%` is missing)
set "SEPARATORS= = " & rem // (characters that separate the words from the rest)
set "FIXEDPOS=#" & rem // (if not empty, defines that `%WORD2%` must be after `%WORD1%`)
rem // Create line-break (carriage-return and line-feed):
(for /F %%# in ('copy /Z "%~f0" nul') do set ^"CR+LF=%%#^
%= empty line =%
^")
rem // Ensure list of separators contains (ends) with space:
if defined SEPARATORS (
if not "%SEPARATORS:~-1%"==" " set "SEPARATORS=%SEPARATORS: =% "
) else set "SEPARATORS= "
setlocal EnableDelayedExpansion
rem // Set up regular expression:
if defined FIXEDPOS (
rem /* `%WORD2%` must be in the line following `%WORD1%`, so define a dual-line
rem regular expression (both words must be present at the beginnings of lines): */
set "REGEX=^%WORD1%[%SEPARATORS%].*!CR+LF!%WORD2%[%SEPARATORS%]"
) else (
rem /* Position of `%WORD2%` does not matter with respect to `%WORD1%`,
rem hence it merely must be present at the beginning of a line: */
set "REGEX=^%WORD2%[%SEPARATORS%]"
)
rem // Search for regular expression in file:
> nul findstr /I /R /C:"!REGEX!" "%FILE%" || (
rem // No match encountered, so read entire file and deplete it afterwards:
for /F "delims=" %%L in ('findstr /N /R "^" "%FILE%" ^& ^> "%FILE%" break') do (
endlocal
rem // Read a line, reset flag that defines whether or not to insert a string:
set "FLAG=" & set "LINE=%%L"
setlocal EnableDelayedExpansion
rem // Split off first word and compare with `%WORD1%`:
for /F "eol= tokens=1 delims=%SEPARATORS%" %%K in ("!LINE:*:=!") do (
endlocal
rem // First word matches `%WORD1%`, so set flag:
if /I "%%K"=="%WORD1%" set "FLAG=#"
setlocal EnableDelayedExpansion
)
rem // Append to file:
>> "%FILE%" (
rem // Write original line:
echo(!LINE:*:=!
rem // Write string to insert in case flag is defined:
if defined FLAG echo(!STRING2!
)
)
)
endlocal
endlocal
exit /B
Note that this script does not check whether str1 occurs multiple times.
Use powershell in your batch file to simplify things
FINDSTR "str2 => bbb" "file.txt"
IF %errorlevel%==0 (
ECHO FOUND
Goto END
)
powershell -Command "(get-content File.txt) -replace "str1 => aaa", "$&`n str2 => bbb" | set-content File.txt"
:end
The powershell command will get the content of your file and replace your string with search string ($&) + new line + str2...)

Displaying lines from text file in a batch file

I'm tryin' to find a script that will let me display "linenumber# and linenumber# as well as lines#-#" from a text file in a batch file? I found this script here on this site..
#echo off
setlocal enabledelayedexpansion
if [%1] == [] goto usage
if [%2] == [] goto usage
SET /a counter=0
for /f "usebackq delims=" %%a in (%2) do (
if "!counter!"=="%1" goto exit
echo %%a
set /a counter+=1
)
goto exit
:usage
echo Usage: head.bat COUNT FILENAME
:exit
And it works great :) But it grabs the number of lines from the top of the text file. I want to be able to display certain lines in the text file, as well as a range if possible..
EG: I have a text file with 30 lines, and I want to display lines 1-4; 7-11; 13; 17-20; 22; 26 & 29
Here's a simple modification of the sample batch file above. Copy the code below to file and name it "LineDisplay.bat" - it takes the FirstLineNumber and LastLineNumber as parameters. Example: LineDisplay test.txt 12 30 (to read lines 12-30)
#echo off
setlocal enabledelayedexpansion
if [%1] == [] goto usage
if [%2] == [] goto usage
if [%3] == [] goto usage
set /a FirstLineNumber = %2
set /a LastLineNumber = %3
echo Reading from Line !FirstLineNumber! to !LastLineNumber!
SET /a counter=1
for /f "usebackq delims=" %%a in (%1) do (
if !counter! GTR !LastLineNumber! goto exit
if !counter! GEQ !FirstLineNumber! echo !counter! %%a
set /a counter+=1
)
goto exit
:usage
echo Usage: LineDisplay.bat FILENAME FirstLineNumber LastLineNumber
:exit
Here's a line to a nice tutorial on creating batch files http://vtatila.kapsi.fi/batch_tutorial.html
Seems to work:
#ECHO OFF
REM Show usage and quit if no file name was given
IF [%1]==[] GOTO USAGE
REM Show entire file if no range was given
IF [%2]==[] TYPE %1 & GOTO :EOF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET FILENAME=%1
SET LASTLINE=0
REM Build the array of lines to display
SHIFT
:RANGEREADLOOP
CALL :BUILDARRAY %1
SHIFT
IF NOT [%1]==[] GOTO RANGEREADLOOP
REM Loop through the file and keep track of the lines to display
SET CURRENTLINE=0
SET INDEX=1
FOR /F "delims=" %%l in (%FILENAME%) DO (
SET LINE=%%l
CALL :PRINTLINE
)
GOTO :EOF
REM Adds the lines from the specified range to the array of lines to display
:BUILDARRAY
REM Find out whether we have a single line or a range
SET TEST=%1
SET /A TEST1=%TEST%
SET /A TEST2=%TEST:-=%
IF %TEST1%==%TEST2% (
REM Single line
SET /A LASTLINE+=1
SET LINES[!LASTLINE!]=%1
) ELSE (
REM Range
FOR /F "tokens=1,2 delims=-" %%x IN ("%1") DO (SET RANGESTART=%%x&SET RANGEEND=%%y)
REM Some sanity checking
IF !RANGESTART! GTR !RANGEEND! (
ECHO.Problem with range !RANGESTART!-!RANGEEND!:
ECHO. Ranges must have a start value smaller than the end value.
EXIT /B 1
) ELSE (
FOR /L %%i IN (!RANGESTART!,1,!RANGEEND!) DO (
SET /A LASTLINE+=1
SET LINES[!LASTLINE!]=%%i
)
)
)
GOTO :EOF
REM Prints the specified line if the current line should be printed
:PRINTLINE
SET /A CURRENTLINE+=1
IF %CURRENTLINE%==!LINES[%INDEX%]! (
REM We have a line to print, so do this
ECHO !LINE!
SET /A INDEX+=1
)
GOTO :EOF
REM Prints usage and exits the batch file
:USAGE
ECHO.%~n0 - Displays selected lines from a text file.
ECHO.
ECHO.Usage:
ECHO. %~n0 ^<filename^> ^<range^> ...
ECHO.
ECHO. ^<filename^> - the text file from which to read
ECHO. ^<range^> - the line range(s) to display.
ECHO.
ECHO.Example:
ECHO. %~n0 foo.txt 1-4 13 15 18-20
ECHO.
ECHO. will display the first four lines from the file "foo.txt",
ECHO. the 13th and 15th as well as the lines 18 to 20.
ECHO.
ECHO.Line ranges are separated by comma, semicolon or space. If no range is given,
ECHO.the entire file is displayed.
EXIT /B 1
GOTO :EOF
The whole script could use some better error checking, examples of what not to do or where error checking is a bit wonky:
dl foo.txt 1-2-4 (prints lines 1-2 but no error message)
dl foo.txt -1 (error message that the range 1- isn't correct)
Other limitations:
Ranges must be sorted. With the current implementation there is no way to do something like dl foo.txt 7-8,1-2.
No line may be selected twice. Something like dl foo.txt 1,2,2-8,11-15 will stop after the second line.
No support for UNIX-style line endings (U+000A)
EDIT: Fixed an issue with files that contain parentheses.

Resources