Find and replace text into txt with batch language - windows

I have a problem with the code ...
I have the files t1.txt and t2.txt. The goal is to move from file 1 to file 2 with the same content unless it contains a specific word, in which case, the entire line must be replaced by a predefined one.
specifically I have problems with the 'if' and with the reassignment of the variable.
#echo off
setlocal enabledelayedexpansion
set p1=t1.txt
set p2=t2.txt
for /f "tokens=*" %%a in (%p1%) do (
set nl=%%a
if not "%n1%"=="%n1:texto=%" (
set n1=replace with this text
)
echo !n1! >> %p2%
)
pause>nul
exit

Your task is possible with pure batch scripting, but it is not that trivial when you want it in a safe manner, with respect to certain special characters and to not lose empty lines of the original file. So here is a way:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_IFILE=t1.txt" & rem // (input file to be processed)
set "_OFILE=t2.txt" & rem // (output file to be returned)
set "_WORD=\<texto\>" & rem // (string to search in lines, `findstr` regular expression)
set "_REPL=replace with this text" & rem // (text to replace lines containing the word)
rem // Output to console if no output file is provided:
if not defined _OFILE set "_OFILE=con"
rem /* Initialise a loop counter which should be synchronous to the line number, unless
rem a line matches the search string, so it becomes skipped and the numbers differ: */
set /A "LCNT=0"
rem // Write to output file:
> "%_OFILE%" (
rem /* Read from input file, prefix every line by a line number + `:`, skip lines
rem matching the search string: */
for /F delims^=^ eol^= %%L in ('cmd /V /C findstr /V /N /R /C:"!_WORD!" "!_IFILE!"') do (
rem // Split off the line number from the line string:
for /F "delims=:" %%K in ("%%L") do (
rem // Increment loop counter:
set /A "LCNT+=1"
rem // Store current line (including line number prefix):
set "LINE=%%L"
rem // Toggle delayed expansion in order to avoid trouble with `!`:
setlocal EnableDelayedExpansion
rem // Check whether loop counter and line number differ:
if !LCNT! lss %%K (
rem /* Numbers differ, hence return the replace line as many times
rem as the difference is: */
set /A "DIFF=%%K-LCNT"
for /L %%D in (1,1,!DIFF!) do echo(!_REPL!
)
rem // Return line with line number prefix removed:
echo(!LINE:*:=!
endlocal
rem // Synchronise loop counter with line number:
set "LCNT=%%K"
)
)
)
endlocal
exit /B
Restrictions:
lines must not exceed a length of about 8190 characters (limited by for /F and variable lengths);
the search expression must not be longer than 254 characters (limited by findstr);
the input text file must be ASCII/ANSI-encoded (cmd might handle other files wrongly, depending on the current code page);
the input text file should not contain characters with a code above 0x7F (cmd might change such characters to others, depending on the current code page);
the input text file must have DOS/Windows-style line endings (Unix-style line endings are understood, but the output text file is going to have DOS/Windows-style line endings then; MAC-style line endings are not supported at all);

The command FOR always ignores empty lines and by default it ignores also lines starting with a semicolon. On usage of delayed expansion also lines containing an exclamation mark are modified on execution of command line set nl=%%a in the loop. So a solution using FOR could be the completely wrong attempt depending on content of file t1.txt.
Download JREPL.BAT written by Dave Benham which is a batch file / JScript hybrid to run a regular expression replace on a file using JScript and store it in same directory as the batch file below.
#echo off
if not exist "%~dp0jrepl.bat" goto :EOF
if not exist "t1.txt" goto :EOF
call "%~dp0jrepl.bat" "^.*textto.*$" "replace with this text" /F "t1.txt" /O "t2.txt"
rem Compare the two files binary with ignoring the differences. Delete file
rem t1.txt if a line was modified by JREPL.BAT. Otherwise move file t1.txt
rem over t2.txt to keep the last modification date of file t1.txt for t2.txt.
%SystemRoot%\System32\fc.exe /B "t1.txt" "t2.txt" >nul
if errorlevel 1 ( del "t1.txt" ) else move /Y "t1.txt" "t2.txt"
jrepl.bat searches with a regular expression for lines containing textto and replaces all such lines by replace with this text. All other lines are copied unmodified from input file t1.txt to output file t2.txt.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
fc /?
goto /?
if /?
move /?
rem /?
jrepl.bat /?

Related

How to run a batch file with a FOR loop with two file names with two parameters?

I am using an alass tool to synchronize two subtitles. It is simple to use with one file at a time but I want to use it on multiple files using a loop.
The usage of the tool is like this:
alass.bat correct_subtitle.srt incorrect_subtitle.srt output.srt
I want to do a simple for loop with two parameters with this command:
FOR %i IN (*g.srt) DO FOR %n IN (*t.srt) DO alass.bat %i %n %n
The script is working but I want the command works one time with the second file not looping the first file with all the second files.
I want the script to do like this:
C:\Users\user\Downloads\alass-windows64\alass.bat Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt
C:\Users\user\Downloads\alass-windows64\alass.bat Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt
etc.
All the subtitles are in one folder the correct and incorrect subtitles are like this:
Correct sub (Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt)
incorrect sub (Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt)
Correct sub (Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt)
incorrect sub (Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt)
etc.
A solution for revision 17 of the question is:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir *.eng.srt /A-D-L /B /ON 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R "\.S[0123456789][0123456789]*E[0123456789][0123456789]*\."') do call :ProcessFile "%%I"
endlocal
exit /B
:ProcessFile
echo Correct file: %1
set "FileNameBegin=%~n1"
:GetMatchingPart
for %%J in ("%FileNameBegin%") do (
echo %%~xJ| %SystemRoot%\System32\findstr.exe /I /R "^\.S[0123456789][0123456789]*E[0123456789][0123456789]*$" >nul
if errorlevel 1 set "FileNameBegin=%%~nJ" & goto GetMatchingPart
)
for /F "eol=| delims=" %%J in ('dir "%FileNameBegin%.*-FGT.srt" /A-D /B 2^>nul') do (
echo Incorrect file: "%%J"
call alass.bat %1 "%%J" "%%J"
)
goto :EOF
That code was run on a FAT32 drive with following files in current directory:
Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt
Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt
Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt
Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt
Exa..mple!.S01E01.anotherName.DTS-FGT.srt
Exa..mple!.S01E01.name.eng.srt
example.S01E02.anotherName-FGT.srt
example.S01E02.name.eng.srt
The output without really calling alass.bat is:
Correct file: "Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt"
Incorrect file: "Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt"
Correct file: "Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt"
Incorrect file: "Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt"
Correct file: "Exa..mple!.S01E01.name.eng.srt"
Incorrect file: "Exa..mple!.S01E01.anotherName.DTS-FGT.srt"
Correct file: "example.S01E02.name.eng.srt"
Incorrect file: "example.S01E02.anotherName-FGT.srt"
The main FOR loop runs in background one more cmd.exe with option /c the command line within ' appended as additional arguments.
The command DIR executed by this second command processor outputs all names of files in current directory matching the wildcard pattern *.eng.srt.
This list is redirected to FINDSTR which filters the list of file names based on the regular expression \.S[0123456789][0123456789]*E[0123456789][0123456789]*\.. So a file name to process must contain a string consisting of
a dot
case-insensitive the letter S
one or more digits in range 0 to 9
case-insensitive the letter E
one or more digits in range 0 to 9
on more dot.
All the file names ending case-insensitive with .eng.srt and matching the regular expression filter criteria are output by FINDSTR to handle STDOUT of background command process and captured by cmd.exe processing the batch file.
The main FOR loop processes the list of file names line by line after the started cmd.exe process closed itself. File names can contain spaces characters which is the reason for using the option delims= to define an empty list of delimiters to turn off the default line splitting behavior on spaces/tabs. File names can start with a semicolon and for that reason the option eol=| is used to define the vertical bar as end of line character which no file name can contain ever. So each file name is assigned completely to the specified loop variable I
For each file name is called the subroutine ProcessFile which first outputs the current file name with correct subtitles.
Next a FOR loop is used to remove from the file name the string after last dot which is the file extension according to the definition of Microsoft. The "file extension" string is tested with FINDSTR on being the part which is used as identifier and also as separator string between film title and the meta data of the film in file name. If regular expression does not return a positive match on the current "file extension" string, the file name is truncated at end by removing the current "file extension".
Finally after one or more loop runs the beginning of the file name is found consisting of film name with zero or more dots inside and the string matched by the regular expression. So the environment variable FileNameBegin is for the four examples:
Batman.Beyond.S01E01
Batman.Beyond.S01E02
Exa..mple!.S01E01
example.S01E02
That string part is now used to find the matching file with incorrect subtitles ending case-insensitive with the string -FGT.srt. That is again done starting one more cmd.exe to run DIR to find that file.
The usage of the command DIR to get a list of matching file names first loaded into memory can be important depending on what alass.bat does with the passed file names. That is important especially on FAT file systems like FAT32 or exFAT which do not store the file names in an local specific alphabetic order. The file tables of the file system can be changed on each call of alass.bat if this batch file modifies the srt files and that is not good on using FOR directly to process the files. It can result in skipping some srt files or processing some srt files more than once or in worst case even in an endless running loop. That is the reason for using DIR executed by a command process in background to always get a list of matching file names which does not change anymore while the main FOR loop as well as the last FOR loop run the commands which perhaps result in changing the file tables of the file system.
That solution is definitely not the fasted possible, but a very fail-safe solution and should work for all film titles and all file systems independent on what alass.bat does as long as this batch file does not change the current directory.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
exit /?
findstr /?
for /?
goto /?
if /?
set /?
setlocal /?
Here's my approach to v17
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the source directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
FOR %%s IN (0 1) DO FOR /L %%t IN (0 1 9) DO FOR %%e IN (0 1) DO FOR /L %%f IN (0 1 9) DO (
FOR /f "delims=" %%o IN (
'dir /b /a-d "%sourcedir%\*.S%%s%%tE%%e%%f.*T.srt" 2^>nul'
) DO (
SET "name=%%~no"
FOR /f "tokens=1,2delims=/" %%p IN ("!name:.S%%s%%tE%%e%%f.=/!") DO (
FOR %%b IN ("%sourcedir%\%%p.S%%s%%tE%%e%%f.*g.srt") do ECHO CALL alass.bat "%%~fb" "%%~fo" "%sourcedir%\%%p.S%%s%%tE%%e%%f.%%q.srt"
)
)
)
GOTO :EOF
Always verify against a test directory before applying to real data.
As ever, the generated lines are simply echoed.
Essentially, for each S and E 00..19, locate the "T" file (otherwise, no point) split the name on the .SssEee. string and find the {1}.SssEee.*g.srt file
mix and match the parts.
...but I still have problems understanding the destination filename...
EDIT: New better version
I added a new version that I think is the fastest and most convenient way to solve this problem:
#echo off
setlocal EnableDelayedExpansion
rem Process "correct" files with eng.srt extension
for %%i in (*.eng.srt) do (
echo Correct sub: %%i
set "correct=%%i"
rem Search for the prefix of this file
set "name="
set "prefix="
for %%k in ("!correct:.=" "!") do if not defined prefix (
set "part=%%~k"
set "name=!name!.!part!"
rem Check if this part have the "S##E##" end of prefix format
if "!part:~0,1!!part:~3,1!!part:~6!" equ "SE" ( rem "S__E__" letters and length match
set /A "S=E=0, S=1!part:~1,2!-100, E=1!part:~4,2!-100" 2>nul
if !S! gtr 0 if !E! gtr 0 ( rem Both ## numbers match: end of prefix found
rem Process the companion "incorrect" *FGT.srt file
set "prefix=!name:~1!"
set "name="
for %%n in (!prefix!.*FGT.srt) do (
echo Incorrect sub: %%n
echo/
REM call alass.bat %%i %%n %%n
set "name=%%n"
)
if not defined name (
echo Warning: Incorrect sub not found
echo/
)
)
)
)
if not defined prefix (
echo Warning: Bad filename format
echo/
)
)
EDIT: New version for the last OP´s revision
This Batch file should solve this question:
#echo off
setlocal EnableDelayedExpansion
rem Count files with same prefix
for %%a in (*.srt) do (
set "name=%%a"
set "prefix="
for %%b in ("!name:.=" "!") do (
set "prefix=!prefix!%%~b."
set /A "count[!prefix:-=_!]+=1"
)
)
rem Process pairs of files
for /F "tokens=2,3 delims=[]=" %%a in ('set count[') do (
if %%b equ 2 (
set "right="
for %%c in ("%%a*.srt") do (
if not defined right (
set "right=%%c"
) else (
set "wrong=%%c"
)
)
call alass.bat !right! !wrong! !wrong!
)
)
Accordingly to your description: "The files have different names like this: Correct sub (example.S01E01.name.ybg.srt). Incorrect sub (differentExample.S01E01.anotherName.wrt.srt)." That is: correct and incorrect names of the same set have the second dot-separated token the same, like S01E01 or S01E02 in the examples shown.
The Batch file below solve such problem:
#echo off
setlocal
for %%i in (*g.srt) do for /F "tokens=2 delims=." %%k in ("%%i") do (
for %%n in (*.%%k.*t.srt) do (
call alass.bat %%i %%n %%n
)
)
NOTE: This part of the answer relates to Revision 17 of the question.
I would do it with the following batch-file, assuming that the first .-separated parts up to the S??E?? pattern of the file names of a pair of files are the same:
#echo off
setlocal EnableExtensions DisableDelayedexpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (target directory)
set "_PREF=*" & rem // (prefix of base file names)
set "_MASK=S??E??" & rem // (middle part of file names without `.`)
set "_FILT=S[0123456789][0123456789]E[0123456789][0123456789]"
set "_SUFF1=*g.srt" & rem // (suffix of 1st file name with extension)
set "_SUFF2=*T.srt" & rem // (suffix of 2nd file name with extension)
set "_TOOL=%~dp0alass.bat"
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Loop over 1st files:
for %%I in ("%_PREF%.%_MASK%.%_SUFF1%") do (
rem // Reset left part of file name, store currently iterated base name:
set "LEFT=" & set "NAME=%%~nI"
setlocal EnableDelayedExpansion
rem // Loop as many times as there are `.`-separated parts in the base name:
for %%K in ("!NAME:.=" "!") do (
rem // Do the following only as long as the left part is still not found:
if not defined LEFT (
rem // Utilise a `for` loop on the base name to yield `~`-modifiers:
for %%L in ("!NAME!") do (
rem /* Split base name into last part and the rest, the latter
rem of which is going to be used for the next iteration: */
endlocal & set "LAST=%%~xL" & set "NAME=%%~nL"
rem // Determine whether the last part matches the given pattern:
cmd /D /V /C echo(!LAST:~1!| findstr /R /X /I /C:"%_FILT%" > nul && (
rem // Match encountered, so store currently processed path:
set "LEFT=%%~nxL"
)
setlocal EnableDelayedExpansion
)
)
)
rem // Procede further only if a suitable left part of file has been found:
for %%L in ("!LEFT!") do endlocal & if not "%%~L"=="" (
rem // Search for respective 2nd file:
for %%J in ("%%~L.%_SUFF2%") do (
rem /* Store names of both 1st and 2nd file, then call the sub-script
rem utilising the second `%`-expansion established by `call` to
rem avoid doubling of `^`-symbols as well as loss of `%`-signs: */
set "FILE1=%%~I" & set "FILE2=%%~J"
call "%_TOOL%" "%%FILE1%%" "%%FILE2%%" "%%FILE2%%"
rem /* Erase 2nd file to prevent reprocessing of same file pairs in
rem case of re-execution of this script (remove `ECHO` first!): */
ECHO del "%%~I"
)
)
)
rem // Return from target directory:
popd
)
endlocal
exit /B
The trick herein is to use the ~-modifiers (namely ~x and ~n in particular) of for-loop meta-variables to split the file names at . from the back within a loop that iterates as many times as there are .-separated parts in the base names.
This approach correctly handles file names with characters !, ^ and %. You can prove that when you create an interim sub-script alass.bat with the following contents:
#echo off
setlocal DisableDelayedExpansion
echo(%0 %*
endlocal
exit /B
In case the tool alass.bat overwrites the original *T.srt files, which is what I assume, the script deletes the *g.srt files (when removing the upper-case ECHO in front of the related command) in order not to reprocess the same pair of files upon re-execution of the script.
NOTE: This part of the answer relates to Revision 9 of the question.
I would do it with the following batch-file:
#echo off
setlocal EnableExtensions DisableDelayedexpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (target directory)
set "_SUFF1=g" & rem // (suffix for base names of 1st files)
set "_SUFF2=T" & rem // (suffix for base names of 2nd files)
set "_MASK=*%_SUFF1%" & rem // (name search pattern for 1st files)
set "_EXT=.srt" & rem // (extensions for 1st and 2nd files)
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Loop over 1st files:
for %%I in ("%_MASK%%_EXT%") do (
rem // Store base name of currently iterated 1st file:
set "NAME=%%~nI"
setlocal EnableDelayedExpansion
rem /* Build base name of respective 2nd file; temporarily appending `|` to the
rem name (which cannot occur in a file name) ensures to just replace the very
rem last occurrence of the suffix: */
set "REPL=!NAME!|" & set "REPL=!REPL:%_SUFF1%|=%_SUFF2%!"
rem // Skip in case there is no respective 2nd file:
if exist "!REPL!!_EXT!" (
rem /* Call sub-script with 1st and 2nd file as input files and 2nd one also
rem as output file, preventing delayed expansion but utilising the second
rem `%`-expansion phase established by `call` in order to avoid doubling
rem of `^`-symbols as well as loss of `%`-signs: */
REM call "%~dp0alass.bat" "!NAME!!_EXT!" "!REPL!!_EXT!" "!REPL!!_EXT!"
call "%~dp0alass.bat" "%%NAME%%%%_EXT%%" "%%REPL%%%%_EXT%%" "%%REPL%%%%_EXT%%"
rem /* Erase 2nd file to prevent reprocessing of same file pairs in case of
rem re-execution of the script: */
ECHO del "!NAME!!_EXT!"
)
endlocal
)
rem // Return from target directory:
popd
)
endlocal
exit /B
This approach correctly handles file names with characters !, ^ and %. You can prove that when you create an interim sub-script alass.bat with the following contents:
#echo off
setlocal DisableDelayedExpansion
echo(%0 %*
endlocal
exit /B
If you used the commented-out call command line (with the upper-case REM in front), ^-symbols would become doubled and %-signs would become lost.
In case the tool alass.bat (which is assumed to reside in the same location as this script) overwrites the original *T.srt files, which is what I assume, the script deletes the *g.srt files (when removing the upper-case ECHO in front of the related command) in order not to reprocess the same pair of files upon re-execution of the script.

Need find command solution for ms-dos application?

I have some *.bat file containing find command to extract some particular line.
for example, if my input text file contains something like:
Login time : XX:XX
username - XXXXXX
Login time : YY:YY
username - YYYYYYY
using username lest say:
find /I "XXXXXX" input.txt | find /I "XXXXXX" > output.txt
I am able to get the username but not sure how to get the correct login time for only the searched user name?
find (and findstr) can't process line feeds. They handle each line on its own. So you have to write a script that remembers the last line, checks the current line for the searchstring and prints both the last line and the current line, if the searchstring is found.
I used findstr instead of find, because it's more secure (find "XXXXXX" would also find XXXXXXY). See findstr /? for the switches i, x and c.
#echo off
setlocal enabledelayedexpansion
set "search=xxxxxx"
for /f "delims=" %%a in (t.txt) do (
echo %%a|findstr /ixc:"username - %search%" >nul && echo !lastline! %%a
set "lastline=%%a"
)
Supposing you are actually working on windows (cmd.exe) rather than on dos (COMMAND.COM), you could use findstr, which allows to define a multi-line search string, although it only returns the first line in case of a match, but the next one is is always the same anyway, so it might not be needed.
This Windows batch-file shows what I mean:
#echo off
rem // Get Carriage-Return (CR) character:
for /F %%C in ('copy /Z "%~f0" nul') do set "CR=%%C"
rem // Get Line-Feed (LF) character:
(set LF=^
%= blank line =%
)
rem // Enable delayed expansion to be able to use CR and LF:
setlocal EnableDelayedExpansion
rem /* Specify a multi-line search string, which must literally reflect the actual
rem line-break (that is CR + LF for a DOS/Windows text file); `findstr` returns
rem only the first line when it encounters a match: */
findstr /I /R /C:"!CR!!LF!username - XXXXXX$" "input.txt" > "output.txt"
rem /* This is needed to get the second line of the multi-line match too
rem (replace this by the commented out line in case you expect multiple entries for
rem the searched user, because you would get multiple equal user name entries; the
rem commented out line ensures that there is a single user name entry at the end): */
findstr /I /R /C:"^username - XXXXXX$" "input.txt" >> "output.txt"
rem findstr /I /R /C:"^username - XXXXXX$" "input.txt" | findstr /I /R /V /C:"!CR!!LF!username" >> "output.txt"
N. B.:
I guess you are using two find commands to get rid of the header that the first one produces; you could however use the following instead:
rem // Input redirection `<` prevents `find` from returning a header:
find /I "XXXXXX" < "input.txt" > "output.txt"

How can I add a new line before a specific line (last line) in text file?

Suppose I have a diskpart script file disk1.txt containing:
list vol
exit
and batch file which contains:
#echo off
pushd %~dp0
diskpart /s disk1.txt
set /p vol=enter number of the volume
echo sel vol %vol% > disk2.txt
type disk1.txt >> disk2.txt
diskpart /s disk2.txt
del disk2.txt
pause
Now I want to add a line in disk2.txt before the last line.
How can I add the new line before the last line?
And how can I add a new line before a specific line, i.e. before line 4 or line 3, or any other specified line?
Here is a simple demo batch code for inserting a line at a specific position within a text file:
#echo off
setlocal EnableDelayedExpansion
rem A negative value inserts a line X lines before last line of source file.
rem A positive value inserts a line before line X from source file.
rem Value 0 assigned to InsertBeforeLine results in a copy of source file.
set "InsertBeforeLine=-1"
if %InsertBeforeLine% LSS 0 (
set "LineCount=1"
for /F "usebackq eol= delims=" %%L in ("disk1.txt") do set /A "LineCount+=1"
set /A "InsertBeforeLine+=LineCount"
)
set "LineCount=1"
for /F "usebackq eol= delims=" %%L in ("disk1.txt") do (
if %InsertBeforeLine% EQU !LineCount! echo Inserted line>>disk2.txt
echo %%L>>disk2.txt
set /A "LineCount+=1"
)
endlocal
Note: This simple batch code does not work for a source file containing lines with no characters or only whitespace characters. In other words each line in disk1.txt must contain at least 1 non whitespace character.

Find a line in a file and replace the next line

Using a .bat script, I want to find a line that says # Site 1 and replace the text in the next line with a variable. I found tutorials on StackOverflow for finding and replacing a line, but not finding a line and replacing the next line. Any help?
#echo off
set "the_file=C:\someFile"
set "search_for=somestring"
set "variable=http://site1"
for /f "tokens=1 delims=:" %%# in ('findstr /n /c:"%search_for%" "%the_file%"') do (
set "line=%%#"
goto :break
)
:break
set /a lineBefore=line-1
set /a nextLine=line+1
break>"%temp%\empty"&&fc "%temp%\empty" "%the_file%" /lb %lineBefore% /t |more +4 | findstr /B /E /V "*****" >newFile
echo %variable%>>newFile
more "%the_file%" +%nextLine% 1>>newFile
echo move /y newFile "%the_file%"
Check if newFile is ok and remove the echo at the front of the last line.
you need to set the three variables at the beginning by yourself.Have on mind that more command sets spaces instead of tabs
#ECHO OFF
SETLOCAL
SET "filename=q28567045.txt"
SET "afterme=# Site 1"
SET "putme=put this line after # Site 1"
SET "skip1="
(
FOR /f "usebackqdelims=" %%a IN ("%filename%") DO (
IF DEFINED skip1 (ECHO(%putme%) ELSE (ECHO(%%a)
SET "skip1="
IF /i "%%a"=="%afterme%" SET skip1=y
)
)>newfile.txt
GOTO :EOF
Produces newfile.txt
The flag `skip1 to skip the line is first reset, then the file is read line by line.
If the skip1 flag is set, then the replacement line is echoed in place of the line read; if not, the line read is echoed.
Then the skip1 flag is cleared
If the line read to %%a matches the string assigned to afterme then the flag skip1 is set (to y - but it doesn't matter what the value is)
Note that empty lines and those starting ; will be ignored and not reproduced - this is standard behaviour of for /f.
If you want to replce the starting file, then simply add
move /y newfile.txt "%filename%"
before the goto :eof line.
Even though I enjoy working with batch, I generally avoid using pure native batch to edit text files because a robust solution is usually complicated and slow.
This can be done easily and efficiently using JREPL.BAT - a hybrid JScript/batch utility that performs regular expression replacement. JREPL.BAT is pure script that runs natively on any Windows machine from XP onward.
#echo off
setlocal
set "newVal=Replacement value"
call jrepl "^.*" "%newValue%" /jbeg "skip=true" /jendln "skip=($txt!='# Site 1')" /f test.txt /o -
The /F option specifies the file to process
The /O option with value of - specifies to replace the original file with the result.
The /JBEG option initializes the command to skip (not replace) each line.
The /JENDLN option checks the value of each line just before it is written out, and sets SKIP off (false) if it matches # Site 1. The next line will be replaced only when SKIP is false.
The search string matches an entire line.
The replacement string is your value stored in a variable.
This problem is similar to this one and may use an equivalent solution. The pure Batch file solution below should be the fastest one of its kind.
#echo off
setlocal EnableDelayedExpansion
set "search=# Site 1"
set "nextLine=Text that replaces next line"
rem Get the line number of the search line
for /F "delims=:" %%a in ('findstr /N /C:"%search%" input.txt') do set /A "numLines=%%a-1"
rem Open a code block to read-input-file/create-output-file
< input.txt (
rem Read the first line
set /P "line="
rem Copy numLines-1 lines
for /L %%i in (1,1,%numLines%) do set /P "line=!line!" & echo/
rem Replace the next line
echo %nextLine%
rem Copy the rest of lines
findstr "^"
) > output.txt
rem Replace input file with created output file
move /Y output.txt input.txt > NUL
This method will fail if the input file have empty lines and also have other limitations.
For a further description of this method, see this post.

Nesting for loop in batch file

I want to nest a for loop inside a batch file to delete carriage return.
I tried it like you can see below but it does not work.
#echo off
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (Listfile.txt) do (
set /a N+=1
set v!N!=%%a
)
for /l %%i in (1, 1, %N%) do (
echo !v%%i!
for /r "tokens=* delims=" %%i in (windows.cpp) do (
echo %%i >> Linux11.cpp
)
)
pause
Here I want to check with windows.cpp. If its working I like to change windows .cpp with !v%%i!
You cannot do this in a batch file. You have no way of addressing or writing arbitrary characters. Every tool on Windows normally makes sure to output Windows line breaks (i.e. CR+LF). Some can read Unix-style line breaks just fine, which is why you can easily convert from them. But to them isn't possible.
Also as a word of caution: Source code files often contain blank lines (at least mine do) that are for readability. for /f skips empty lines which is why you're mangling the files for your human readers there. Please don't do that.
As for your question: When nesting two loops you have to make sure that they don't use the same loop variable. Show me a language where code like you wrote actually works.
Something like
for /l %%i in (1, 1, %N%) do (
echo !v%%i!
for /f "tokens=* delims=" %%l in ("!v%%i!") do (
rem do whatever you want to do with the lines
)
)
should probably work better (you missed the final closing parenthesis as well). Thing to remember: If you want to use a certain variable instead of a fixed file name it surely helps replacing that fixed file name by that variable.
It would be probably easiest to use some unix2dos/dos2unix converter to do that or some win32 flavor of sed.
The intrinsic issue of your code is already addressed by another answer, hence I am going to focus on the main task you are trying to accomplish, namely converting DOS/Windows-style end-of-line markers (or line-breaks) to Unix-style ones.
Doing this is very tricky in a batch file, but give the following script a try. Supposing it is called convert.bat, and the original text file is named convert.txt, run the script using the following command line:
convert.bat "convert.txt" LF
The name of the returned file will get the original file name with _converted_EOL appended. The second argument LF specifies Unix-style line-breaks; omitting it will return DOS/Windows-style ones.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem check whether or not an existing file is given as the first argument
>&2 (
if "%~1"=="" (
echo No file specified.
exit /B 2
) else if not exist "%~1" (
echo File "%~1" not found.
exit /B 1
)
)
rem get carriage-return character
for /F %%A in ('copy /Z "%~0" nul') do set "CR=%%A"
rem get line-feed character (the two empty lines afterwards are mandatory!)
(set ^"LF=^
%= blank line =%
^")
rem check which line-break is given by the second argument
rem (`CR` - carriage return (Mac); `LF` - line feed (Unix);
rem anything else or nothing - CR+LF (Windows, default))
setlocal EnableDelayedexpansion
set "BR=!CR!!LF!"
if /I "%~2"=="CR" set "BR=!CR!" & (>&2 echo CR not supported.) & exit /B 3
if /I "%~2"=="LF" set "BR=!LF!"
rem convert line-breaks; append `_converted_EOL` to file name
setlocal DisableDelayedExpansion
> "%~n1_converted_EOL%~x1" (
for /F delims^=^ eol^= %%L in ('
findstr /N /R "^" "%~1"
') do (
set "LINE=%%L"
rem firstly, precede every line with a dummy character (`:`) and
rem append the specified line-break in order to avoid the loss of
rem leading white-spaces or trouble with leading equal-to signs,
rem all caused by `set /P`, which is needed here to return the
rem line without a trailing DOS/Windows-style line-break (opposed
rem to `echo`); then, let `pause` strip off that character;
rem lastly, let `findstr` return the remainder;
rem (the `rem` suffix is just there to fix syntax highlighting)
cmd /V /C ^< nul set /P #="!LINE:*:=:!!BR!" | (> nul pause & findstr "^") & rem/ "^"
)
)
endlocal
endlocal
endlocal
exit /B
The following restrictions apply:
no line must be longer than about 8190 characters (this is a general limitation of batch files);
the file must not contain any null-bytes (well, a normal text file should not hold such, but Unicode-encoded do);
the last line of the returned file will always be terminated by a line-break, even if the respective original line is not;
And here is another solution for line-break conversions: Convert all CR to CRLF in text file using CMD

Resources