Find and Replace matching string in file using batch script - batch-file

I have written a simple script which will find and replace a matching string. I am getting issue in the output file. Following is my script
#echo off
setlocal ENABLEDELAYEDEXPANSION
for /f "tokens=1 delims=" %%a in ('FINDSTR "^applicationPort" "E:\BATCH-SCRIPTING\Version.txt"') do (
echo Installed version is %%a
set oldPort=%%a
)
for /f "tokens=1 delims=" %%b in ('FINDSTR "^applicationPort" "E:\BATCH-SCRIPTING\Sample.txt"') do (
echo Installed version is %%b
set newPort=%%b
)
set SEARCHTEXT="applicationPort=8080"
set REPLACETEXT="applicationPort=8090"
set file="E:\BATCH-SCRIPTING\Sample.txt"
for /f "tokens=1,* delims=]" %%A in ('"type %file% |find /n /v """') do (
set "line=%%B"
if defined line (
call echo %%line:%SEARCHTEXT%=%REPLACETEXT%%%>> %file%_new
) ELSE echo.
)
move /Y %file%_new %file% > nul
In my output file the expected output should be : applicationPort=8090, but i am getting 8080=applicationPort=8090=8080.
Can anyone help me in resolving this issue.

Look at what your key replacement line would like like after the first round of variable expansion:
call echo %%line:%SEARCHTEXT%=%REPLACETEXT%%%>> %file%_new
becomes
call echo %line:applicationPort=8080=applicationPort=8090%>>"E:\BATCH-SCRIPTING\Sample.txt"
The search/replace parser ends the search phrase at the first = it sees. So after the CALL it searches for applicationPort and replaces it with 8080=applicationPort=8090. Perfectly logical.
The bad news is there is absolutely no way to escape the = in the search term. Replacing = is extremely difficult using a batch file. There are solutions, but I don't recommend them. Instead, I would use my hybrid JScript/batch utility called REPL.BAT. It is pure script that will run on any Windows machine from XP onward. It performs a regex search and replace on stdin and writes the result to stdout. Full documentation is embedded within the script.
#echo off
type "E:\BATCH-SCRIPTING\Sample.txt"|repl "applicationPort=8080" "applicationPort=8090" >"E:\BATCH-SCRIPTING\Sample.txt_new"
move /y "E:\BATCH-SCRIPTING\Sample.txt_new" "E:\BATCH-SCRIPTING\Sample.txt"
REPL.BAT is much simpler and faster (and more powerful) than anything you can do using pure batch script.

Related

How can I loop through Windows Services and compare them with an text file in batch

I did a batch code which is a security check, to see if any system services have been modified.
This works through a previously registered text file, which contains the name of each Windows service.
AJRouter
ALG
AppIDSvc
Appinfo
AppReadiness
AppXSvc
AudioEndpointBuilder
Audiosrv
autotimesvc
AxInstSV
BDESVC
BFE
BITS
...
I managed to do the part where the code loops on each line of the text file, and checks if any Windows services are missing.
#echo off
for /F "tokens=*" %%A in (WindowsServices.txt) do (
SC QUERY %%A > NUL
IF ERRORLEVEL 1060 (ECHO %%A IS MISSING)
)
PAUSE
I would also like my code to check if there is any new service that is not registered in the text file. After a few attempts, I couldn't find a way to make my idea work. How can I do this?
#echo off
( for /f "tokens=1,*" %%A in (
'sc query type^= service type^= userservice state^= all ^| find "SERVICE_NAME:"'
) do #echo %%B
) > all.tmp
( for /f "tokens=*" %%A in (
'findstr /b /e /i /l /v /g:"WindowsServices.txt" "all.tmp"'
) do #echo + %%A
for /f "tokens=*" %%A in (
'findstr /b /e /i /l /v /g:"all.tmp" "WindowsServices.txt"'
) do #echo - %%A
for /f "tokens=*" %%A in (
'findstr /b /e /i /l /g:"all.tmp" "WindowsServices.txt"'
) do #echo %%A
) > report.txt
del all.tmp
sort /+3 report.txt /o report.txt
type report.txt
Using an output format that maybe easier to read and easier to process with a script if you want to further processing with the output.
Output interpret as:
If leads with +, needs adding to WindowsServices.txt i.e. New.
If leads with -, is in WindowsServices.txt though not in registry i.e. Missing.
If leads with space, is in WindowsServices.txt and in registry i.e. Good.
If the last item listed is not wanted i.e. Good, then remove the last for loop. I added it for completeness so that all service names are accounted for.
findstr uses:
/b Matches pattern if at the beginning of a line.
/e Matches pattern if at the end of a line.
/i Specifies that the search is not to be case-sensitive.
/l Uses search strings literally.
/g Get search strings from the specified file.
sort gets all the service names in alphabetical order.
Note: Earlier Windows OSes may not have a userservice type for sc.exe, such as Windows 7. The code works on Windows 10 without modification.

Reading large file from CMD and cutting information

I'm writing batch script on Windows, with it's help I would like to sort out information from many files to smaller files.
I got ~3000 long lines in log files, from whom I need get few things, basically there are name and value (example ",INC_LIMI=050,ISO_LIMI=050,INC_MLIM=074,"), and everything is separated with "," symbol. My question how you can read long string line and just read values like:
String LineString[]
LineString = Line.split(,)
String s = "INC_MLIM"
For elem in LineString
if elem.exist(s)
NewLine.append(elem)
and latter on just save to new file.
EDIT:
There is service.log file which contains multiple lines with same variable names, but I don't need all of them so the thing I'm trying to do is
From line :
",INC_MLIM=074,ISO_MLIM=074,LOC_LI_P=050,LOC_LI_L=050,TRI_LI_P=074,TRI_LI_L=074,"
Transform to new line structure with less variables and separate with tabs instead of comma. New line should look something like this:
"INC_MLIM=074 ISO_MLIM=074 LOC_LI_L=050 TRI_LI_L=074"
You don't state which values you want. I'll arbitrarily assume you want INC_LIMI and INC_MLIM.
Like most any text file manipulation, this is a pain to do with pure batch. But it is possible.
I'm assuming your lines are all <8192 characters long. If you have lines that are longer than that, then a pure batch solution is not possible, and you should skip right down to the bottom of this answer for a JREPL solution
Batch does not have a convenient split function that allows splitting at a specific user defined character. The FOR command almost works, but it also splits at ;, =, <tab>, and <space>. So it is not a good choice.
With the correct arcane syntax, you can use variable expansion find/replace to substitute a newline (0x0A) for every comma. This will generate one name=value pair per line, which is very convenient for letting FINDSTR filter out the values that you want.
Here is a solution that relies on a temporary table. This iterates all *.log files, and for each one, it creates output in *.log.new.
#echo off
setlocal enableDelayedExpansion
(set LF=^
%= This creates a newline 0x0A character =%
)
for %%N in ("!LF!") do for %%F in (*.log) do (
(
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=%%A"
echo(!ln:,=%%~N!
)
)>"%%F.temp"
findstr /b "INC_LIMI= INC_MLIM=" "%%F.temp" >"%%F.new"
del "%%F.temp"
)
type *.log.new
exit /b
Note that the above can fail if your log files contain !. This could be solved by toggling delayed expansion on and off as needed.
Some people don't like to use temp files. In this case, getting rid of the temp file introduces even more arcane batch constructs. But it does eliminate the ! delayed expansion issue, and the code is shorter. This version can also be significantly slower if the source files are very large.
#echo off
setlocal disableDelayedExpansion
(set LF=^
%= This creates a newline 0x0A character =%
)
for %%F in (*.log) do (
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=%%A"
cmd /v:on /c "for %%N in ("!LF!") do #echo(!ln:,=%%~N!"|findstr /b "INC_LIMI= INC_MLIM="
)
) >"%%F.new"
type *.log.new
exit /b
It is also possible to solve this without using FINDSTR. But this solution assumes the same name never appears more than once on any given line, and all found names have a value:
#echo off
setlocal disableDelayedExpansion
for %%F in (*.log) do (
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=,%%A"
for %%N in (INC_LIMI INC_MLIM) do call :findName %%N
)
) >"%%F.new"
type *.log.new
exit /b
:findName Name
setlocal enableDelayedExpansion
set "test=!ln!"
:loop
set "test2=!test:*,%1=!"
if "!test2!" equ "!test!" return
if not defined test2 return
if "!test2:~0,1!" neq "=" set "test=,!test2:*,=!" & goto :loop
for /f "delims=," %%V in ("!test2:~1!") do (
endlocal
echo(%1=%%V
)
exit /b
Here is a variation that handles empty values, but can break if a value contains quotes or poison characters:
#echo off
setlocal disableDelayedExpansion
for %%F in (*.log) do (
for /f "usebackq delims=" %%A in ("%%F") do (
set "ln=,%%A"
for %%N in (INC_LIMI INC_MLIM) do call :findName %%N
)
) >"%%F.new"
type *.log.new
exit /b
:findName Name
setlocal enableDelayedExpansion
set "test=!ln!"
:loop
set "test2=!test:*,%1=!"
if "!test2!" equ "!test!" return
if not defined test2 return
if "!test2:~0,1!" neq "=" set "test=,!test2:*,=!" & goto :loop
set "test2=%1!test2!
endlocal&echo(%test2:,=&rem %
exit /b
But I wouldn't use any of the above. In fact, I would never restrict myself to pure batch because text file manipulation is so darn inefficient and inscrutable.
Instead, I would use JREPL.BAT - a regular expression command line text processing utility. JREPL.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward - no 3rd party exe file required.
With JREPL, the solution is as simple as
#echo off
for %%F in (*.log) do call jrepl "(?:^|,)((?:INC_LIMI|INC_MLIM)=[^,]*)" "$txt=$1" /jmatchq /f "%%F" /o "%%F.new"
type *.log.new
Not only is the code nice and clean, it is much faster than any pure batch solution.

Replacing a string in a bunch of text files in a windows folder

Need help ! Working on a batch file which will replace a set of characters from a bunch of text files in a folder. I have found the code which will do that. But it does for only one file. IS there a way where it can do it for all the files in the folder. There a total of 1000 files inside the folder. I am using a Windows 7 OS. Attaching the code I found where it does for one file.
Thanks
Harry
#echo off
setlocal enabledelayedexpansion
set INTEXTFILE=Replace_string.txt
set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=Apple
set REPLACETEXT=Mango
set SEARCHTEXT=Cat
set REPLACETEXT=Dog
set OUTPUTLINE=
for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %INTEXTFILE%"') do (
SET string=%%A
for /f "delims=: tokens=1,*" %%a in ("!string!") do set "string=%%b"
if "!string!" == "" (
echo.>>%OUTTEXTFILE%
) else (
SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
echo !modified! >> %OUTTEXTFILE%
)
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
If you want a pure batch solution, then the simplest thing to do is to encapsulate the code in a subroutine that takes the name of the file as an argument, and call that routine from within a FOR loop that iterates the file names. This is a general approach that can be used for any code that you want to run iteratively
against files in a folder.
The end result of your code does not create or rename any folders, so a simple FOR is safe to use. But if your code creates or renames folders, then a FOR loop could also process the newly created or renamed files. This can be solved by using FOR /F with the DIR /B command instead.
Note - I eliminated dead (unused) variables from the code.
#echo off
setlocal enabledelayedexpansion
pushd "c:\path\to\your\folder\containing\files\to\modify"
for %%F in (*.txt) do call :replace "%%F"
exit /b
:replace
set INTEXTFILE=%1
set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=Cat
set REPLACETEXT=Dog
for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %INTEXTFILE%"') do (
SET string=%%A
for /f "delims=: tokens=1,*" %%a in ("!string!") do set "string=%%b"
if "!string!" == "" (
echo.>>%OUTTEXTFILE%
) else (
SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
echo !modified! >> %OUTTEXTFILE%
)
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
exit /b
But there are many limitations and inefficiencies with the code, with lots of room for improvement.
Limitations:
Input and output lines must be a bit less than 8191 bytes long.
The search ignores case
The search string cannot contain = or begin with ~, * or !
The replace string cannot contain !
Lines containing ! will be corrupted because delayed expansion is enabled when %%A is expanded. This can be solved by strategically toggling delayed expansion on and off within the loop(s).
Leading : will be stripped from all lines because consecutive delimiter characters are treated as a single delimiter.
The replacement will be corrupted if the search term contains %%a or %%b or %%A. This can be avoided by transferring the search and replacement terms to FOR variables.
Certain characters within the search and/or replacement terms could cause problems or require complex escape sequences. This can be simplified by getting the desired strings in environment variables (which may still require escape sequences) and then using delayed expansion and FOR /F to transfer the values to FOR variables.
There are obscure situations where ECHO. can fail. The only safe variant that is guaranteed to work is ECHO(.
A non empty line could become empty after replacement if the replacement string is empty, and the empty line will not be output properly because neither ECHO. nor ECHO( was used.
Inefficiencies / other issues
Redirection is performed for each line of output, which is slow. Better (faster) to redirect once outside the loop.
The DEL/RENAME pair can be replaced by a single MOVE command
CALL is relatively slow. Best to minimize use of CALL if you want the fastest possible solution. But sometimes it cannot be avoided.
I prefer to have my temp file name to be a derivative of the original name. I typically append a .new extension to the original name, so "original.txt" becomes "original.txt.new"
Below is highly optimized code that addresses all but the first 4 points above. It is about as robust and efficient as pure batch can get if you want to use FOR /F to read the file.
#echo off
setlocal disableDelayedExpansion
pushd "c:\path\to\your\folder\containing\files\to\modify"
set "find=Cat"
set "repl=Dog"
setlocal enableDelayedExpansion
for /f delims^=^ eol^= %%S in ("!find!") do (
for /f delims^=^ eol^= %%R in ("!repl!") do (
endlocal
for %%F in (*.txt) do (
for /f "delims=" %%L in ('findstr /n "^" "%%F"') do (
set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
if defined ln set "ln=!ln:%%S=%%R!"
echo(!ln!
endlocal
)
) >"%%F.new" & move /y "%%F.new" "%%F" >nul
)
)
popd
The above required a lot of experience and arcane knowledge to develop. Even after all the work, it still has the following limitations.
Input and output lines must be a bit less than 8191 bytes long.
The search ignores case
The search string cannot contain = or begin with ~, * or !
The replace string cannot contain !
Removing those limitations would require a ridiculous amount of slow and even more impenetrable code. Hardly worth it.
That is why I have abandoned using pure batch for text processing, and I developed JREPL.BAT to provide powerful regular expression text processing capabilities to the Windows command environment. JREPL is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.
JREPL makes the solution so simple, it can easily be run directly on the command line, without any batch file.
pushd "c:\your\path\with\files\to\be\modified"
for %F in (*.txt) do call jrepl "Cat" "Dog" /l /f "%F" /o -
popd
The search is case sensitive. You can add the /i option if you want the search to ignore case. Change the %F to %%F if you use the command within a batch script.
Since all you need to do is replace text in an existing file, use a tool like FART which is specifically designed to do this:
FART -r "C:\Data\Directory\*.txt" "OldText" "NewText"
The -r switch says to process all txt files in provided directory and subfolders.
Note that you can add the -i switch to ignore the case when searching.

Editing text in a file using a batch file

I am trying to edit some lines of text within a text file using a batch file. The lines are as below -
#TEST_RSA_KEY=1
#V3SERVER0=109.73.122.107;29006
#DOWNLOAD0=109.73.122.112;29006
I need to change these to be
#TEST_RSA_KEY=0
#V3SERVER0=91.207.36.31;29006
#DOWNLOAD0=91.207.36.37;29006
How would you recommend I do this via a batch file, I am very new to this and have very basic knowledge so simple and clear answers please! :) Thank you
Not tested
#echo off
set file_loc=C:\text.file.txt
for "usebackq tokens=* delims=" %%a in ("%file_loc%") do (
set "%%a"
)
set "#TEST_RSA_KEY=0"
set "#V3SERVER0=91.207.36.31;29006"
set "#DOWNLOAD0=91.207.36.37;29006"
set #>"%file_loc%"
I've assumed that all properties start with #
Note - I have not tested any of the code examples below, so there could be bugs. But I have successfully used all of the techniques below in the past, and any fixes (if necessary) should be fairly trivial.
All of my solutions below assume that spaces never appear in any of the name value pairs. Each of the solutions could be adapted to support spaces, if necessary.
If the order of the lines is not important, then the following pure batch solution works well and is quite fast. I use FINDSTR to remove the lines that are to be altered, before appending the new values to the end:
#echo off
>"file.txt.new" (
findstr /v /b "#TEST_RSA_KEY= #V3SERVER0= #DOWNLOAD0=" "file.txt"
echo #TEST_RSA_KEY=0
echo #V3SERVER0=91.207.36.31;29006
echo #DOWNLOAD0=91.207.36.37;29006
)
move /y "file.txt.new" "file.txt" >nul
If the order of the lines is important, then I would use my JREPL.BAT utility - a hybrid JScript/batch regular expression text processor. It is pure script that runs natively on any Windows machine from XP onward. I recommend putting the script within a directory that is listed within your path. I like to use "c:\utils" for all my non-standard utilities.
A simple one replacement at a time strategy is the simplest effective solution, and it should be plenty fast unless the file is exceptionally large:
#echo off
call jrepl "^(#TEST_RSA_KEY)=.*" "$1=0" /f "file.txt" /o -
call jrepl "^(#V3SERVER0)=.*" "$1=91.207.36.31;29006" /f "file.txt" /o -
call jrepl "^(#DOWNLOAD0)=.*" "$1=91.207.36.37;29006" /f "file.txt" /o -
The above strategy can be made easy to maintain (add additional replacements) with a bit more code, as long as none of the strings contain * or ?, and no value begins with =:
#echo off
for %%A in (
"#TEST_RSA_KEY=0"
"#V3SERVER0=91.207.36.31;29006"
"#DOWNLOAD0=91.207.36.37;29006"
) do for /f "tokens=1* delims==" %%B in (%%A) do (
call jrepl "^(%%B)=.*" "$1=%%C" /f "file.txt" /o -
)
The * and ? limitation can be removed if the replacement strings are already in a separate file (replace.txt):
#echo off
for /f "tokens=1* delims==" %%A in (replace.txt) do (
call jrepl "^(%%A)=.*" "$1=%%B" /f "file.txt" /o -
)
Or the replacement strings could be embedded within the batch script itself
#echo off
for /f "tokens=1* delims==" %%A in ('findstr "^#" "%~f0"') do (
call jrepl "^(%%A)=.*" "$1=%%B" /f "file.txt" /o -
)
exit /b
#TEST_RSA_KEY=0
#V3SERVER0=91.207.36.31;29006
#DOWNLOAD0=91.207.36.37;29006
The entire job can be done much more efficiently in one pass if you are willing to do the capture group bookkeeping and prepare a long command line (8191 byte length max):
#call jrepl "(#TEST_RSA_KEY)=.* (#V3SERVER0)=.* (#DOWNLOAD0)=.*" "$2=0 $4=91.207.36.31;29006 $6=91.207.36.37;29006" /b /t " " /f "file.txt" /o -
A variation of any of the prior 3 methods could be used to make this efficient solution easier to maintain, as long as ! never appears in the values. For example:
#echo off
setlocal enableDelayedExpansion
set "find="
set "repl="
set "n=0"
for /f "tokens=1* delims==" %%A in ('findstr "^#" "%~f0"') do (
set "find=!find! (%%A)=.*"
set /a n+=2
set "repl=!repl! $!n!=%%B"
)
call jrepl "!find:~1!" "!repl:~1!" /b /t " " /f "file.txt" /o -
exit /b
#TEST_RSA_KEY=0
#V3SERVER0=91.207.36.31;29006
#DOWNLOAD0=91.207.36.37;29006

Replace a character in a file using a batch script

I am new to batch file scripting and need to develop a script to replace a character in a file using a batch script.
I have to replace "servername/ActionService.asmx,1"
with "servername/ActionService.asmx,0"
in file called APP.
Please let me know if there is any solution using only commands.
You can use GNUWin32 sed:
#ECHO OFF &SETLOCAL
set "string=servername/ActionService.asmx,1"
FOR /f %%a IN ('echo "%string%" ^| sed "s/[0-9]/0/"') DO set "newstring=%%~a"
ECHO %newstring%
If you're toggling back and forth between these two states, it might be easier to create two copies of the file with different names, together with a couple of batch files (e.g. actionService1.bat and actionService2.bat) to copy the appropriate file over your APP file.
Otherwise you might consider getting Windows versions of the Unix tools sed and awk, which excel at this type of file manipulation.
The Batch file below assume that there is precisely one line with the target string. This method is relatively fast.
#echo off
for /F "delims=:" %%a in ('findstr /N "servername/ActionService.asmx,1" theFile.txt') do set lineNum=%%a
(for /F "tokens=1* delims=:" %%a in ('findstr /N "^" theFile.txt do (
set "line=%%b"
setlocal EnableDelayedExpansion
if %%a equ %lineNum% (
echo !line:1=0!
) else (
echo(!line!
)
endlocal
)) > theFile.new
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
:: Way the first - suppresses emptylines
FOR /f "delims=" %%i IN (app) DO SET line=%%i&set line=!line:servername/ActionService.asmx,1=servername/ActionService.asmx,0!&ECHO(!line!
ECHO ====================
:: Way the second
FOR /f "delims=" %%i IN ('type app^|findstr /n "$"') DO (
SET line=%%i
set line=!line:servername/ActionService.asmx,1=servername/ActionService.asmx,0!
SET line=!line:*:=!
ECHO(!line!
)
ECHO ====================
GOTO :EOF
Two ways here. You would need to redirect your choice to a new file as you cannot update in-place.
Doing a quick google search I found this http://www.dostips.com/?t=Batch.FindAndReplace
Using a helper batch file called repl.bat from here: http://www.dostips.com/forum/viewtopic.php?f=3&t=3855
type app |repl "servername\/ActionService.asmx,1" "servername/ActionService.asmx,0" >appnew.txt

Resources