Bat file to remove data from text file - batch-file

I have bat file which split my data from one row into multiple rows as expected but it is taking unnecessary data as well.
Data in txt file-
Data will be in same format as below
85:85123, ,Smith,181215:image.tif,image2.tif,image3.tif
85:85456, ,Richard,184985:image4.tif,image5.tif
85:85789, ,Aly,589456:image.tif
Output I am getting is:
123,
123,smith
123,image.tif
123,image2.tif
123,image3.tif
456,
456,Richard
456,image4.tif
456,image5.tif
789,
789,Aly
789,image.tif
Output which i want:
123,image.tif
123,image2.tif
123,image3.tif
456,image4.tif
456,image5.tif
789,image.tif
I only want unique number with image name on every row but I am getting unique number with one blank value and first name as well.
#echo off
> "Output.csv" (
for /F "usebackq tokens=1-2* delims=: " %%A in ("transact.txt") do (
set "NUM=%%B" & set "LIST=%%C"
setlocal EnableDelayedExpansion
for %%D in ("!NUM:*%%A=!") do (
for %%E in ("!LIST:,=" "!") do (
endlocal
echo(%%~D %%~E
setlocal EnableDelayedExpansion
)
)
endlocal
)
)

Well, you seem to want to filter out all files whose names are not image followed by an optional number and followed by .tif, which could be done by inserting a bit of code (see rem remarks):
#echo off
> "Output.csv" (
for /F "usebackq tokens=1-2* delims=:, " %%A in ("transact.txt") do (
set "NUM=%%B" & set "LIST=%%C"
setlocal EnableDelayedExpansion
for %%D in ("!NUM:*%%A=!") do (
for %%E in ("!LIST:,=" "!") do (
endlocal
rem // Store several interim values into variables:
set "ITEM=%%~nE" & set "EXT=%%~xE" & set "NUM=%%~D"
setlocal EnableDelayedExpansion
rem /* Split file name at first numeric part; the preceding `_` just
rem ensures that the part before the first number is not empty,
rem because then files like `012image3.tif` became handled wrongly;
rem everything up to the first colon `:` becomes removed: */
for /F "tokens=1* delims=0123456789" %%I in ("_!ITEM:*:=!") do (
endlocal
rem /* Ensure that the part behind the first numeric part (`%%J`) is
rem empty, that the name part in front of the number is `image`,
rem and that the file extension is `.tif`: */
if "%%J" == "" if /I "%%I" == "_image" if /I "%%~xE" == ".tif" (
setlocal EnableDelayedExpansion
echo(!NUM!,!ITEM:*:=!!EXT!
endlocal
)
setlocal EnableDelayedExpansion
)
)
)
endlocal
)
)

Just 3 minimal changes are necessary, to get your desired output.
:: Q:\Test\2019\09\09\SO_57856028.cmd
#echo off
> "Output.csv" (
for /F "usebackq tokens=1-4* delims=:, " %%A in ("transact.txt") do (
set "NUM=%%B" & set "LIST=%%E"
setlocal EnableDelayedExpansion
for %%D in ("!NUM:*%%A=!") do (
for %%E in ("!LIST:,=" "!") do (
endlocal
echo(%%~D,%%~E
setlocal EnableDelayedExpansion
)
)
endlocal
)
)

Related

Batch remove part of string after “-1” is found or any other number “-[0-9]”

I got a file containing a string on each line like this:
fruit-apple-1.5.6
vegtable-sla-mc5-6.5-16515
extra-huh-9.5-511-515
extra-3.2
I am iterating over it and want it to remove the part of the string on the right after in find "-" + any number "-0","-1","-2","-9",...
so output should be
fruit-apple
vegtable-sla-mc5
extra-huh
extra
this is code i have but it only works with a "-" i cant combine it so it takes "-" + any number like "-1","-5","-2",...
for /f "delims=|" %%A in ("!fileNameCheck:-=|!") do (
echo stripped string = %%A
)
complete code not necessary i think but in case u need it below
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set "RawPath=%~dp0"
FOR /F "USEBACKQ TOKENS=*" %%M IN ("%RawPath%/mods") DO (
REM for %%f in (*.jar) do (
Set "fileNameCheck=%%M"
for /f "delims=|" %%A in ("!fileNameCheck:-=|!") do (
Echo [46m%%A[0m
if exist "%~dp0%%A*.jar" (
REM echo [32mFound %%A "%~dp0%%A*.jar"[0m
if exist "%~dp0%%M" (
REM echo [42mUp to Date[0m [32m%%A "%~dp0%%M"[0m
) else (
for %%j in (*.jar) do (
echo %%j |find "%%A" >nul
if not errorlevel 1 (
echo [41mDifferent Version[0m [31m%%j [0m[90mNewer version[0m [32m%%M[0m
)
)
)
) else (
REM echo [31mMissing %%A[0m
)
)
)
pause
Given that the strings do not contain any of the caracters ?, *, < and >, the following script should do the trick:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0."
set "_FILE=mods.txt"
rem // Read file line by line:
for /F usebackq^ delims^=^ eol^= %%L in ("%_FILE%") do (
rem // Store current line, initialise variables:
set "LINE=%%L" & set "PREV=-" & set "COLL=-"
setlocal EnableDelayedExpansion
rem /* Split line string at every `-` and iterate through items;
rem (`?`, `*`, `<`, `>` must not occur in the string!): */
for %%I in ("!LINE:-=" "!") do (
endlocal & set "ITEM=%%~I"
rem // Check whether item only consists of numerals and `.`:
(
rem // This loop executes when there are other characters:
for /F "delims=0123456789. eol=." %%J in ("%%~I") do (
setlocal EnableDelayedExpansion
rem /* Rebuilt string with currently found items; if there
rem are other items following numeric ones, keep them: */
for /F "delims=" %%K in ("!COLL!!PREV:~1!-!ITEM!") do (
endlocal & set "COLL=%%K" & set "PREV=-"
)
)
) || (
rem // This section runs when there are numerals and `.`:
setlocal EnableDelayedExpansion
for /F "delims=" %%K in ("!PREV!-!ITEM!") do (
rem /* Store numeric items in a separate buffer; if there
rem are no other items following, this is dismissed: */
endlocal & set "PREV=%%K"
)
)
setlocal EnableDelayedExpansion
)
rem // Return rebuilt line string:
echo(!COLL:~2!
endlocal
)
endlocal
exit /B
The example input file mods.txt:
fruit-apple-1.5.6
vegtable-sla-mc5-6.5
extra-huh-9.5
extra-3.2
test-9.15.5
keep-forge
name-subname-10.5-55.5
globalxp-1.16.5
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more-3.4
mix-1.2-string-10.5-55.5-more-3.4-5.6-7
8.9
1.2.3-lead
1.2.3-4.5.6-7.8.9-lead-mult
leads to the following output:
fruit-apple
vegtable-sla-mc5
extra-huh
extra
test
keep-forge
name-subname
globalxp
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more
1.2.3-lead
1.2.3-4.5.6-7.8.9-lead-mult
One way is to replace the - with \ and then use variable substitution to strip the %%~ni part from the string. We just need to remove the first and last occurrence then from the string. findstr will determine that the end of string needs to be -num.*
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%A in ('dir /b /s *.jar ^| findstr /RC:"-[0-9].*$"') do (
set "line=%%~nxA"
for %%i in ("!line:-=\!") do set "final=%%~pi"
set "final=!final:~0,-1!-"
echo !final! | findstr /RC:"\\[0-9].*$" >nul 2>&1
if !errorlevel! equ 0 (
for %%s in ("!final!") do set "final=%%~ps"
set final=!final!
)
set "final=!final:~0,-1!"
set "final=!final:~1!"
echo !final:\=-!
)
the edited code should take care of the additional - as per your comment example name-subname-10.5-55.5
Here's an untested idea, which may not be particularly quick, especially if you have a large number of strings in your mods file:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F %%G In ('Echo Prompt $E ^| %SystemRoot%\System32\cmd.exe /D'
) Do Set "$E=%%G"
For /F Delims^=^ EOL^=^ UseBackQ %%G In ("%~dp0mods") Do (Set "Line=%%G"
SetLocal EnableDelayedExpansion
For /L %%H In (0 1 9) Do For /F Delims^=^ EOL^= %%I In (
'(Echo(^) ^| Set /P "=!Line:-%%H="^&:"!" 0^<NUL') Do Set "Line=%%I"
Echo(%$E%[46m!Line!%$E%[0m
EndLocal
)
Pause

Removing carriage returns from a text file using a batch file based on a value

I have a text file that I would like to edit and therefore would like to remove the last line. I have the following code for this:
for /f "delims=" %%a in (input.txt) do (
echo/|set /p ="%%a%"
)>>output.txt
input:
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
output:
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
Now I would like to edit the data in groups for example by the first value, so that I have the following output:
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
If I replace the FOR /F "delims=" %%a in (input.txt) do … loop with an equivalent FOR %%a in … loop:
#ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
set "_gruppeName="
(
for %%a in (
"GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
"GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;"
) do (
for /f "tokens=1 delims=;" %%A in ("%%~a") do (
if /I NOT "%%~A"=="!_gruppeName!" (
if defined _gruppeName echo(
set "_gruppeName=%%~A"
)
)
echo/|set /p ="%%~a"
)
echo(
)>>output.txt
REM debugging output follows
type output.txt
Output:
1st run: 2>NUL del output.txt & D:\bat\CR\61816520.bat
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
Next run: D:\bat\CR\61816520.bat
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEA;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEB;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;GRUPPEC;123;12345;sdfdsfds;sdfdsfsdfs;sdfsdfafsf;
Your question is not clear
based on ... the first value (GRUPPEA)
is it SORTed? or just write duplicates on the same line?
#echo off
SETLOCAL EnableExtensions EnableDelayedExpansion
::VARIABLES
set "in=input.txt" IN file
set "out=output.txt" OUT file
set/a"#lines=0"
set "gruppe="
set "prevContent="
::Count lines
FOR /F %%L in ('
"findstr /N "^^" "%in%" %= do not skip empty lines =%"
') do set/a"#lines+=1" %= Get the # of lines =%
::Read IN via SET /P #LINES number of times
::Bangs (!) will be lost
<"%in%" >"%out%" (FOR /L %%# in (1 1 %#lines%) do (
set "data=" ::clear DATA
set/p"data=" ::read from IN
FOR /F tokens^=1^ delims^=^;^ eol^= %%T in ("!data!") do set "gruppe=%%T"
if NOT "!prevContent!" == "!gruppe!" (
set "prevContent=!gruppe!"
echo(
)
<nul set/p"=!data!" ::does not work with leading space, tabs, or equal signs
)) %= read file by lines via SET /P =%
exit /b
The script counts the number of lines using FINDSTR /N and a FOR /F loop to count the # of lines, which is required to execute SET /P that many times.
Tips:
Use ECHO( instead of the problematic ECHO.
Using a pipe | is very slow, as described by #jeb here. Use <nul instead

Replace a matching string in each line with another string

I am trying to replace the number at the end of every line of a given file with an incremented number(+1).
My Input file looks like
C:\documents\a.txt,1988,15.01.00.0059
C:\documents\we.txt,1988,15.01.00.0059
C:\documents\gh.txt,1987,1988,15.01.00.0059
my out put file should be
C:\documents\a.txt,1988,15.01.00.0060
C:\documents\we.txt,1988,15.01.00.0060
C:\documents\gh.txt,1988,15.01.00.0060
My attempt is like
#echo off
setlocal enableextensions disabledelayedexpansion
set "search="
for /F "tokens=1-3 delims=," %%i in (%1) do (
#echo %%i %%j %%k
set "search=%%k"
)
set "replace="
REM #echo %replace%
set "textFile=%1"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
>>"%textFile%" echo(!line:%search%=%replace%!
endlocal
)
i could able to get 15.01.00.0059 from the input file but dont know how to change to 15.01.00.0060 .
replacing to the same input file is also working . I just need to know how to increment the value and assign to "replace".
Thank you
Bhargav.k
This should be flexible enough:
#echo off
setlocal EnableDelayedExpansion EnableExtensions
1>out.txt (
FOR /F tokens^=*^ delims^=.^ eol^= %%L in (input.txt) do (
set "line=%%L"
set "x=!line:.= !"
FOR %%T in (!x!) do set "lastToken=%%T"
cmd /c exit /b !lastToken!
set/a"lstrip=!ERRORLEVEL!+1"
>getlen.tmp echo(!lasttoken!
FOR %%L in (getlen.tmp) do set/a"len=%%~zL-2" %= get length of last token, minus CRLF =%
FOR %%N in (1 1 !len!) do set "lstrip=0!lstrip!" PREFIX lstrip with zeroes
FOR %%N in (!len!) do (
set "lstrip=!lstrip:~-%%N!"
echo !line:~0,-%%N!!lstrip!
)
)
)
del getlen.tmp
ENDLOCAL
This is not a straightforward approach.
The script counts the # of zeroes prefixed before the last token by the ERRORLEVEL returned by EXIT /B, compared with the original token.

New CSV by combining 2 csv files

I have 2 CSV files that has
File1:
Column1,column2
data1, data2
File2:
Column3,column4, column5,column6
data3,data4,data5,data6
I have to create a new CSV file that combines both columns from file 1 with the 1st and 3rd columns from file 2 (4 total columns).
Column1,column2,column3,column5
data1,data2,data3,data5
I am looking to do this using batch file commands. Any suggestions?
Code i am using helps me copy one file.
#ECHO OFF
SETLOCAL
(
FOR /f "tokens=1-3delims=," %%a IN (file1.csv) DO (
ECHO(%%a,%%c
)
)>new.csv
GOTO :EOF
How about following script?
File1.csv :
column1,column2
data1,data2
data3,data4
data5,data6
File2.csv :
column3,column4,column5,column6
data3,data4,data5,data6
data7,data8,data9,data10
Script :
#echo off
setlocal enabledelayedexpansion
set ct1=0
for /f "tokens=*" %%i in (File1.csv) do (
set /a ct1+=1
set ar1[!ct1!]=%%i
)
set ct2=0
for /f "tokens=*" %%i in (File2.csv) do (
set /a ct2+=1
set ar2[!ct2!]=%%i
)
if !ct1! lss !ct2! (
set ct=!ct2!
) else (
set ct=!ct1!
)
for /l %%i in (1,1,!ct!) do (
echo !ar1[%%i]!,!ar2[%%i]!>> new.csv
)
new.csv :
column1,column2,column3,column4,column5,column6
data1,data2,data3,data4,data5,data6
data3,data4,data7,data8,data9,data10
data5,data6,
Here is a pure batch solution that works, but with the following limitations and or assumptions:
File 1 lines are terminated by carriage return and linefeed (Windows style)
File 1 lines are no longer than 1021 bytes
File 2 must have a value for each column (no consecutive commas)
File 2 line lengths never exceed ~8191 bytes
File 2 does not have any quoted column values that include commas.
Files 1 and 2 have the same number of lines
Neither file has quoted data values that include new lines (rare, but possible within a CSV).
#echo off
setlocal disableDelayedExpansion
<"file1.csv" >"merged.csv" (
for /f "usebackq eol=, delims=, tokens=1,3" %%A in ("file2.csv") do (
set /p "part1="
set "part2=%%A,%%B"
setlocal enableDelayedExpansion
echo !part1!,!part2!
endlocal
)
)
A much more robust and faster solution is possible if you use PowerShell, JScript, or VBS.
You can also implement an efficient and robust solution using JREPL.BAT - a regular expression text processing utility. JREPL.BAT is pure script (hybrid batch/JScript) that runs natively on any Windows machine from XP onward. Full documentation is available from the command line via jrepl /?, or jrepl /?? for paged help.
This JREPL solution has only the following reasonable limitations:
Files 1 and 2 must have the same number of lines
Neither file has quoted data values that include new lines
#echo off
setlocal
set "col=\q(?:\q\q|[^,\q])*\q|[^,]*"
call jrepl "^(%col%),(?:%col%),(%col%)(?:,.*|$)" ^
"$txt=stdin.ReadLine()+','+$1+','+$2" ^
/jq /x /f "file2.csv" /o "merged.csv" <"file1.csv"
This flexible script does what you want, given that the following restrictions are not violated:
both files must contain the same number of lines;
the number of columns per line/row must be equal per each file;
lines are no longer than 1023 bytes, including the terminating line-break;
field/cell values must not contain line-breaks;
each line/row must be terminated by Windows-style line-breaks (CR+LF);
the given column numbers to copy must be sorted in ascending order;
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE1=%~dp0File1.csv" & rem // (1st input CSV file; state `%~1` to use 1st arg.)
set "_FILE2=%~dp0File2.csv" & rem // (2nd input CSV file; state `%~2` to use 2nd arg.)
set "_COLS1=1,2" & rem // (ascending list of columns to copy from 1st file)
set "_COLS2=1,3" & rem // (ascending list of columns to copy from 2nd file)
set "_SEPAR=," & rem // (separator character, usually `,`)
rem // Main routine:
4< "%_FILE1%" 3< "%_FILE2%" (
call :READLINE
)
endlocal
exit /B
:READLINE
rem // Read a line of both files:
set "LINE1=" & set "LINE2="
<&4 set /P LINE1=""
<&3 set /P LINE2=""
rem // Terminate sub-routine in case both lines are empty:
if not defined LINE1 if not defined LINE2 exit /B
rem // Process lines:
call :PROCESS LINE1 LINE2
rem // Repeat reading:
goto :READLINE
exit /B
:PROCESS ref_string1 ref_string2
setlocal DisableDelayedExpansion
set "BUF=%_SEPAR%"
setlocal EnableDelayedExpansion
rem // Test both strings against global wild-card characters:
set "STR1=!%~1!" & set "STR2=!%~2!"
if "!STR1:**=!!STR2:**=!"=="!STR1!!STR2!" goto :PROCESS_CONT
if "!STR1:*?=!!STR2:*?=!"=="!STR1!!STR2!" goto :PROCESS_CONT
if "!STR1:*<=!!STR2:*<=!"=="!STR1!!STR2!" goto :PROCESS_CONT
if "!STR1:*>=!!STR2:*>=!"=="!STR1!!STR2!" goto :PROCESS_CONT
>&2 echo(ERROR: Illegal character encountered^^!
exit /B 1
:PROCESS_CONT
rem // Prepare line strings for being processed by a standard `for` loop:
set "STR1=!STR1:"=""!^"
set "STR2=!STR2:"=""!^"
set "STR1="!STR1:%_SEPAR%=","!""
set "STR2="!STR2:%_SEPAR%=","!""
rem // `for /F` loops to transport prepared line strings beyond `endlocal`:
for /F "delims=" %%E in (^""!STR1!"^") do (
for /F "delims=" %%F in (^""!STR2!"^") do (
endlocal
rem // Process 1st line string:
set /A "IDX=0"
for %%I in (%%~E) do (
rem // Compare column index of current item with given column list:
set /A "IDX+=1" & set "FND="
for %%J in (%_COLS1%) do (
setlocal EnableDelayedExpansion
if !IDX! EQU %%J (
endlocal & set "FND=#"
) else endlocal
)
rem // Matching column index encountered, so assemble output line:
if defined FND (
set "NEW=%%~I%_SEPAR%"
setlocal EnableDelayedExpansion
for /F "delims=" %%K in (^""!BUF!!NEW!"^") do (
endlocal
set "BUF=%%~K"
)
)
)
rem // Process 1st line string:
set /A "IDX=0"
for %%I in (%%~F) do (
rem // Compare column index of current item with given column list:
set /A "IDX+=1" & set "FND="
for %%J in (%_COLS2%) do (
setlocal EnableDelayedExpansion
if !IDX! EQU %%J (
endlocal & set "FND=#"
) else endlocal
)
rem // Matching column index encountered, so assemble output line:
if defined FND (
set "NEW=%%~I%_SEPAR%"
setlocal EnableDelayedExpansion
for /F "delims=" %%K in (^""!BUF!!NEW!"^") do (
endlocal
set "BUF=%%~K"
)
)
)
setlocal EnableDelayedExpansion
)
)
rem // Return output line buffer:
echo(!BUF:~1,-1!
endlocal
endlocal
exit /B

Including Double Quotes in Batch Search and Replace script

I have a config file on many remote machines that I need to modify through the use of a batch script. The config file has two lines like this:
1_IP = "10.101.34.216"
2_IP = "10.101.34.214"
I simply need to swap the two IP's, but I'm having a hard time.
Ideally, I would write a simple script to search for 10.101.34.216 and replace it with 10.101.34.214 and vice versa - however, if I accidentally run the script on the remote machines in the future it would just revert to the original.
Therefore, I need to set the search parameter to look for exactly 1_IP = "10.101.34.216" and replace it with exactly 1_IP = "10.101.34.214" and then subsequently an exact search for 2_IP = "10.101.34.214" to be replaced with exactly 2_IP = "10.101.34.216"
I'm currently using the following script, which I found on a separate stackoverflow post.
#echo off
setlocal enableextensions disabledelayedexpansion
set "search=%1"
set "replace=%2"
set "textFile=system.cfg"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
However, if I set search parameter to
set search= "1_IP = "10.101.34.216" "
It does not work because of the double quotes. I've tried several approaches to escape the double quotes, but I can't seem to get it to work. Any suggestions?
Thank you in advance for your time, patience, and expertise.
Since the current strings behind 1_IP = and 2_IP = do not seem to matter, I would not try to replace them, but I would simply write the desired strings, like this (see all the explanatory rem comments):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~dp0system.cfg" & rem // (path/name of file to process)
set "_KEY[1]=1_IP" & rem // (1st key to search for)
set "_KEY[2]=2_IP" & rem // (2nd key to search for)
set "_VAL[1]=10.101.34.214" & rem // (1st value to assign to 1st key)
set "_VAL[2]=10.101.34.216" & rem // (2nd value to assign to 2nd key)
rem // Read specified file and iterate through all (non-empty) lines:
for /F delims^=^ eol^= %%L in ('type "%_FILE%" ^& ^> "%_FILE%" rem/') do (
rem // Write (append) to the specified file:
>> "%_FILE%" (
rem // Split key from value (key must not contain spaces):
for /F "eol== delims== " %%K in ("%%L") do (
rem /* If key is a predefined one return respective value;
rem otherwise return the current line unedited: */
if /I "%%K"=="%_KEY[1]%" (
echo(%%K = "%_VAL[1]%"
) else if /I "%%K"=="%_KEY[2]%" (
echo(%%K = "%_VAL[2]%"
) else (
echo(%%L
)
)
)
)
endlocal
exit /B
If you want to be able to predefine an arbitrary number of keys and values, and to retain empty lines, you may want to use this script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~dp0system.cfg" & rem // (path/name of file to process)
set "_KEY[1]=1_IP" & rem // (1st key to search for)
set "_KEY[2]=2_IP" & rem // (2nd key to search for)
set "_VAL[1]=10.101.34.214" & rem // (1st value to assign to 1st key)
set "_VAL[2]=10.101.34.216" & rem // (2nd value to assign to 2nd key)
rem // Read specified file and iterate through all lines, preceded by line number:
for /F "delims=" %%L in ('findstr /N "^" "%_FILE%" ^& ^> "%_FILE%" rem/') do (
rem // Write (append) to the specified file:
>> "%_FILE%" (
rem // Split line number plus key from value (key must not contain spaces):
for /F "delims== " %%K in ("%%L") do (
rem // Store extracted key and full line, both including line numbers:
set "KEY=%%K" & set "LINE=%%L"
setlocal EnableDelayedExpansion
rem // Clear flag, remove line number from key:
set "FLAG=" & set "KEY=!KEY:*:=!"
rem // Loop through all available keys:
for /F "tokens=2 delims=[]=" %%M in ('2^> nul set _KEY[') do (
rem /* If key is a predefined one return respective value;
rem otherwise set flag to indicate key has been found: */
if /I "!KEY!"=="!_KEY[%%M]!%" (
echo(!KEY! = "!_VAL[%%M]!"
set "FLAG=#"
)
)
rem // Return current line unedited in case flag is not set:
if not defined FLAG echo(!LINE:*:=!
endlocal
)
)
)
endlocal
exit /B
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q41578841.txt"
SET "outfile=%destdir%\outfile.txt"
SET "search1=1_IP = \"10.101.34.216\""
SET "search2=2_IP = \"10.101.34.214\""
SET "replace1=1_IP = "10.101.34.214""
SET "replace2=2_IP = "10.101.34.216""
SET "replaced=N"
(
FOR /f "usebackqtokens=1*delims=" %%a IN ("%filename1%") DO (
ECHO(%%a|FINDSTR /x /L /C:"%search1%" >NUL
IF ERRORLEVEL 1 (
ECHO(%%a|FINDSTR /x /L /C:"%search2%" >NUL
IF ERRORLEVEL 1 (ECHO(%%a
) ELSE (SET "replaced=Y"&ECHO(%replace2%
)
) ELSE (SET "replaced=Y"&ECHO(%replace1%
)
)
)>"%outfile%"
IF %replaced%==Y (MOVE "%outfile%" "%filename1%">nul&ECHO made changes
) ELSE (DEL "%outfile%"&echo no changes made
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
I used a file named q41578841.txt containing your data plus some dummy data for my testing.
%outfile% may be used as a temporary file. its name is not relevant, it just needs to not-exist when the job is run.
read each file line. if the line exactly matches (/x) the search-string (/c: since it may contain spaces, /L literally - /i for case-insensitive omitted) then set errorlevel 0. if neither matches, regurgitate the line, else output the replacement line and flag that the replacement took place.
Finally, either replace the file or delete the dummy output file and report.
It's not pretty, but when is Windows Shell script?...:
#echo off
setlocal
set TEXT_FILE=.\system.cfg
set IP_1=
set IP_2=
for /f "tokens=*" %%L in (%TEXT_FILE%) do call :PROCESS_LINE %%L
echo 1_IP = "%IP_2%"
echo 2_IP = "%IP_1%"
endlocal
goto END
:PROCESS_LINE
set PL_LINE=%*
set PL_LINE=%PL_LINE:"=%
if "%PL_LINE:~0,4%" == "1_IP" set IP_1=%PL_LINE:~7%
if "%PL_LINE:~0,4%" == "2_IP" set IP_2=%PL_LINE:~7%
goto END
:END

Resources