Extract a specific portion from a string(filename) using batch file - batch-file

I am trying to extract a portion of all the filenames(pdf files) in the current directory.
The length of filenames vary except for the last portion(datetime and extension) which will always be 16 characters. The remaining part will always have different lengths. Even the portion I require may have varying lengths.
I tried using lastIndexOf function obtained here.
filename eg : academyo-nonpo-2582365-082416051750.pdf
I want to extract the section in Bold.
I tried trimming the last 17 characters(this portion will always have a fixed length.) first and then tried to obtain the last Index Of '-'(since the fist portion can have variable character length.) and trim the characters until that position, which should return the required portion of the filename.
#echo off
Setlocal enabledelayedexpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call :lastindexof !File! - lio2
Set "File=!File:~%lio%!"
)
Pause&Exit
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal enableDelayedExpansion
set "str=%~1"
set "p=!str:%~2=&echo.!"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
set argv=original
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
set "str=A!%%~2!"%\n%
echo -1
)
setlocal DisableDelayedExpansion
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
%$strlen% strlen,str
%$strlen% plen,last_part
%$strlen% slen,splitter
set /a lio=strlen-plen-slen
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b
The reference of the variable passed to the function as the 3rd parameter doesn't seem to be returning the required value.
I dunno what is wrong here.

To get the section in bold then:
Example#
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~-20,7!"
Echo=!File!%%~x#)
Pause
Okay what about?
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~,-13!"
Call :Sub "!File:-=\!%%~x#")
Pause
:Sub
Echo=%~nx1

To extract the portion in between the last hyphen and the next-to-last one, you could use the following script (provide the strings/files as command line arguments):
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "SEP=-"
for %%A in (%*) do (
set "ITEM=%%~A"
set "PREV="
if defined ITEM (
for %%B in ("!ITEM:%SEP%=" "!") do (
set "PREV=!PART!"
set "PART=%%~B"
)
if defined PREV (
echo(!PREV!
)
)
)
endlocal
exit /B
This approach basically replaces every - by the standard cmd tokenisation character SPACE and iterates through the resulting string using a standard for loop (no /F option). The currently iterated part is stored in variable PART, whose content is first copied into PREV to gain a delay of one loop iteration. So the next-to-last portion is finally stored in PREV.
Note that this script might return unexpected results in case the strings/files contain exclamation marks because of delayed expansion.

Have a look on this answer. Thought is to first count the number of tokens (you still do have to trim the string before this) and then get the last token.
In the first loop where it says "tokens=1*" , you have to edit it to the following: "tokens=1* delims=-" and in the second loop add delims=- as well after %i%. It should be looking like this in total with your script:
#echo off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call:subfunction !File! - lio2
Set "File=!File:~%lio%!"
)
:subfunction
set var1=%1
set var2=%var1%
set i=0
:loopprocess
for /F "tokens=1* delims=-" %%A in ( "%var1%" ) do (
set /A i+=1
set var1=%%B
goto loopprocess )
for /F "tokens=%i% delims=-" %%G in ( "%var2%" ) do set last=%%G
echo %last%
REM do what you want with last here!
I tested it and it seems to be working correctly even with something like ac-ade-myo-n-on-po-15482729242321654-082416051750.pdf, however after finishing correctly, it give an error message one time with a syntax error I could not find...
If you can ignore that error (everything else works), this might help.

Related

Double Expansion on Array Index Windows Batch

Inside the for loop I'm trying to access the element at index count in CLs (this line of code: echo !!CLs[!count!]!!) , but I'm not sure how to do this. I don't really understand how expansion works in this case, so what you see below it me trying something out of no where.
#ECHO off
setlocal enableextensions enabledelayedexpansion
SET CLs[0]=#
SET /A count = 0
FOR /F "tokens=5" %%I IN ('some command') DO (
echo !!CLs[!count!]!! :: THIS LINE
IF NOT %%I == CLs[!count!] (
SET /A count += 1
SET CLs[!count!]=%%I
)
)
echo The item is %CLs[10]%
endlocal
Thanks
According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts? (see phase 5), the line echo !!CLs[!count!]!! cannot work, because the opening !! are collapsed to a single !, then !CLs[! is expanded to an empty string (assuming such variable is not defined), then count is returned literally, then !]! is expanded to an empty string and the final ! is dismissed. Or in other words, delayed expansion cannot be nested.
You can use call though to introduce another parsing phase, like this:
call echo %%CLs[!count!]%%
The line IF NOT %%I == CLs[!count!] ( ... ) is wrong, you must expand the right value too. However, call if will not help unfortunately, because if (like for and rem) is a special command that is recognised by the parser earlier than others, like call.
To work around that you can store the value of !count! in a for meta-variable, like %%J, for instance, to introduce another parsing phase, and use !CLs[%%J]! then, like this:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
for %%J in (!count!) do (
echo !CLs[%%J]!
if not "%%I" == "!CLs[%%J]!" (
set /A "count+=1"
set "CLs[!count!]=%%I"
)
)
)
Another yet slower possibility is to put the relevant code into a sub-routine:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
call :SUB !count!
)
goto :EOF
:SUB
echo !CLs[%~1]!
if not "%%I" == "!CLs[%~1]!" (
set /A "count+=1"
set "CLs[%~1]=%%I"
)
goto :EOF
You may also take a look at the post Arrays, linked lists and other data structures in cmd.exe (batch) script about how to deal with such pseudo-arrays.
ECHO ------------- START AT %time%
REM <!-- language: lang-dos -->
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /f "tokens=1,2,3delims=[]=" %%a IN ('set cls[') DO IF /i "%%a"=="cls" (
IF "%%I"=="%%c" (SET /a clscnt[%%b]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
ECHO -------------------------Second way -----------------
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /L %%a IN (0,1,!count!) DO (
IF "%%I"=="!cls[%%a]!" (SET /a clscnt[%%a]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
GOTO :EOF
I used a file named q58209698.txt containing some dummy data for my testing and chose to use the entire data line, having no suitable files where token 5 existed.
Note that as a bonus, I've added clscnt - an array of occurence-counts.
Shown: two separate ways of achieving the aim of finding/counting the unique tokens. Naturally, if the cls array is pre-loaded with the required tokens, then it's basic-programmer's-play to adjust the code to detect/report occurrences of those tokens.
The two methods are similar. In the first, set is used to list the established variables starting cls[. The first if ensures processing only the array-name cls, then either it's a repeat (set prcoessed to a value and increment the occurrences-counter) or it's a new value (when the for...%%a loop ends, processed is still undefined) so record it.
The second way is more direct, using the value of count to specifically interrogate the values in the cls array.

How to return an array of values across ENDLOCAL

I have two variable length arrays of values, TargetName[] and TargetCpu[], which I need to return across the ENDLOCAL boundary. I've tried the following, but only the first value on the first array gets returned.
for /L %%i in (0,1,%MaxIndex%) do (
for /f "delims=" %%j in (""!TargetName[%%i]!"") do (
for /f "delims=" %%k in (""!TargetCpu[%%i]!"") do (
endlocal
set TargetName[%%i]=%%j
set TargetCpu[%%i]=%%k
)
)
)
Below is a print of the values returned.
Number Targets: 3
TargetName[0]: "Computer1"
TargetCpu[0] : "x64"
TargetName[1]: "!TargetName[1]!"
TargetCpu[1] : "!TargetCpu[1]!"
TargetName[2]: "!TargetName[2]!"
TargetCpu[2] : "!TargetCpu[2]!"
I've read about everything I can find, but nothing I've tried works for a variable length array.
#echo off
setlocal
set "MaxIndex=6"
call :CreateArrays
set TargetName
set TargetCPU
goto :EOF
:CreateArrays
setlocal EnableDelayedExpansion
for /L %%i in (1,1,%MaxIndex%) do (
set /A TargetName[%%i]=!random!, TargetCpu[%%i]=!random!
)
rem Return the arrays to the calling scope
set "currentScope=1"
for /F "delims=" %%a in ('set TargetName[ ^& set TargetCPU[') do (
if defined currentScope endlocal
set "%%a"
)
exit /B
set target>tempfile
rem insert your endlocal here
for /f "delims=" %%a in (tempfile) do set "%%a"
set target
the first set will list all variable names that start target into a tempfile.
Then execute your endlocal
then read each line of the file, which is of the form name=value and execute it prefixed by the set keyword.
Final set is to display results.
clearing up the tempfile isyour affair. Naturally, if you have other elements you don't want restored, you could use for instance
set targetname>>tempfile
set targetcpu>>tempfile

Batch script - Wrong If syntax

I am trying to check the text in my created array, if I am not using "if" every thing works and i can use "echo" but when I add the "if" command I get "wrong syntax"
#echo off
setlocal ENABLEDELAYEDEXPANSION
set i=0
for /f "delims= " %%a in ('command') do (
set /A i+=1
set list[!i!]=%%~a
)
set Filesx=%i%
rem Display array elements
for /L %%i in (1,1,%Filesx%) do (
if list[%%i] =="some ttext"
echo !list[%%i]!
)
I would consider changing your script accordingly:
#Echo Off
SetLocal EnableDelayedExpansion
Set "i=0"
For /F %%A In ('command') Do (Set/A i+=1
Set list[!i!]=%%~A)
Rem Display array elements
For /L %%A In (1,1,%i%) Do If /I "!list[%%A]!"=="some ttext" Echo !list[%%A]!
Pause
In your script you needed to change if list[%%i] to If /I "!list[%%i]!"
#echo off
setlocal ENABLEDELAYEDEXPANSION
set i=0
for /f "delims= " %%a in ('command') do (
set /A i+=1
set list[!i!]=%%~a
)
set Filesx=%i%
rem Display array elements
for /L %%i in (1,1,%Filesx%) do (
if /i "!list[%%i]!" =="some ttext" (
echo !list[%%i]!
)
)
An if statement requires an exact match (for == or equ) so if you quote one side, you need to quote the other.
also, the action for the if-true condition must be on the same physical line as the if

Rename first part of filename using batch

I have some problem writing a code for a batchfile that will replace the first part of a file name.
let say we have the files:
abcd123.txt
abcd345.txt
the numeric part(and the extensions) is the part I want to keep and change it to blabla123.txt and blabla345.txt
the numeric part is not always the same.
I tried to write:
set FILE =%1
set LastPart = %FILE:~-7%
set NewName = c:\MyFolder\blabla%LastPart%
ren %FILE% %NewName%
but it didn't worked because there's space between c:\MyFolder\blabla to 123.txt
Perhaps:
SET "OldName=%~n1"
SET "Ext=%~x1"
SET "LastPart=%OldName:~-3%"
SET "FirstPart=blabla
SET "NewFold=C:\MyFolder"
REN "%~1" "%NewFold%\%FirstPart%%LastPart%%Ext%"
Please see if below script helps you. It iterates through all files in a given directory and renames them according to your requirement.
#echo OFF
setlocal ENABLEDELAYEDEXPANSION
REM Get input directory from user
set /p INPUT_DIR=Please enter full path to directory with files, use double quotes if any space:
cd /d %INPUT_DIR%
for /f %%f in ('dir /b %INPUT_DIR%') do (
set newname=hello!fullname:~-7!
ren %%f !newname!
)
Output
E:>dir /b "E:\Temporary\SO\batch\Input - Space"
adadadadad123.txt
E:>Temporary\SO\batch\test_ren.bat
Please enter full path to directory with files, use double quotes if any
space:"E:\Temporary\SO\batch\Input - Space"
E:>dir /b "E:\Temporary\SO\batch\Input - Space"
hello123.txt
Although the question is not quite clear to me, I decided to provide an answer, because the task of extracting a numeric part from the end of a string appears not to be that trivial, particularly in case both the preceding string and the numeric portions may have different lengths.
So here is a script that accepts file paths/names/patterns provided as command line arguments, splits off ther numeric part, prepends an optional prefix to it and renames the file accordingly (actually it just echoes the ren command line for testing; remove the upper-case ECHO to actually rename):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PREFIX="
for %%F in (%*) do (
for /F "tokens=1-2 delims=0123456789 eol=0" %%K in ("_%%~nF") do (
if "%%L"=="" (
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
set "FNEW="
setlocal EnableDelayedExpansion
set "FILE=_!FILE!"
for /L %%E in (0,1,9) do (
set "NAME=!FILE:*%%E=%%E!"
if not "!NAME!"=="!FILE!" (
if 1!NAME! GTR 1!FNEW! (
set "FNEW=!NAME!"
)
)
)
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
endlocal
)
)
)
endlocal
exit /B
The script skips all files that have less or more than exactly one numeric part in their names, and also those where the numeric part is followed by something other than the file name extension. For example, abcd1234.txt is processed, whereas abcd.txt, 1234.txt, ab1234cd.txt, 1234abcd.txt and ab12cd34.txt are skipped. Note that the numeric part is limited to nine decimal figures.
If the limit of nine digits is disturbing, the following script can be used. It is very similar to the aforementioned one, but a numeric comparison has been replaced by a string comparison with the numbers padded by leading zeroes to have equal lengths. Therefore the string comparison provides the same result as a pure numeric comparison:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PREFIX="
set /A "DIGS=256"
setlocal EnableDelayedExpansion
for /L %%E in (1,1,%DIGS%) do set "PADZ=!PADZ!0"
endlocal & set "PADZ=%PADZ%"
for %%F in (%*) do (
for /F "tokens=1-2 delims=0123456789 eol=0" %%K in ("_%%~nF") do (
if "%%L"=="" (
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
set "FNEW="
setlocal EnableDelayedExpansion
set "FILE=_!FILE!"
for /L %%E in (0,1,9) do (
set "NAME=!FILE:*%%E=%%E!"
if not "!NAME!"=="!FILE!" (
set "CMPN=%PADZ%!NAME!"
set "CMPF=%PADZ%!FNEW!"
if "!CMPN:~-%DIGS%!" GTR "!CMPF:~-%DIGS%!" (
set "FNEW=!NAME!"
)
)
)
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
endlocal
)
)
)
endlocal
exit /B
This is a robust and more flexible approach, which allows to specify what numeric part to extract by its (zero-based) index, in the variable INDEX (a negative value counts from the back, so -1 points to the last one, if you prefer that):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "PREFIX=blah" & rem // (optional prefix to be used for the new file names)
set /A "INDEX=0" & rem // (`0` means first numeric part, `-1` means last one)
rem // Loop through command line arguments:
for %%F in (%*) do (
set /A "CNT=-1" & set "KIND="
for /F "delims== eol=" %%E in ('2^> nul set "$PART["') do set "%%E="
rem // Store information about currently iterated file:
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Assemble a list of file name portions of numeric and non-numeric parts:
set "LIST= "!FILE!" "
for /L %%J in (0,1,9) do set "LIST=!LIST:%%J=" %%J "!"
set "LIST=!LIST: "" =!"
rem // Determine file name portions, together with their count and kinds:
for %%I in (!LIST!) do (
endlocal & set /A "CNT+=1"
set "ITEM=%%~I" & set "TEST=%%I"
setlocal EnableDelayedExpansion
if "!TEST!"=="!ITEM!" (set "KND=0") else (set "KND=-")
for /F %%K in ("KIND=!KIND!!KND!") do (
for /F "delims=" %%E in ("$PART[!CNT!]=!ITEM!") do (
endlocal & set "%%K" & set "%%E"
)
)
setlocal EnableDelayedExpansion
)
rem // Retrieve the desired numeric file name portion:
if %INDEX% lss 0 (set /A "INDEX=-(1+INDEX)")
if %INDEX% lss 0 (set "RANGE=!CNT!,-1,0") else (set "RANGE=0,1,!CNT!")
set /A "IDX=-1" & set "FNEW=" & for /L %%J in (!RANGE!) do (
if "!KIND:~%%J,1!"=="0" set /A "IDX+=1" & (
if !IDX! equ !INDEX! for %%I in (!IDX!) do set "FNEW=!$PART[%%J]!"
)
)
rem // Actually rename file:
if defined FNEW (
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
)
endlocal
)
endlocal
exit /B

Find & Replace string using for /f with if statement and variables

I have written a batch file which I want to overwrite key strings with strings from another .txt file.
currently it copies the new File.txt file perfectly but does not replace the strings with the strings from OldFile.txt file.
example of strings in File.txt file:
...
# Password
Pword=
# AccountName
Account=
# TownName
Town=
# Postcode
Postcode=
# LocationChangedDate
LocationChanged=
example of strings in OldFile.txt file I want to replace from:
...
# Password
Pword=ABC
# AccountName
Account=123
# TownName
Town=LDN
# Postcode
Postcode=WS77TP
# LocationChangedDate
LocationChanged=01/01/2015
Can someone please point me in the right direction or explain where I have made a mistake?
#echo off
setlocal disableDelayedExpansion
::Variables
set InputFile=F:\EXCHANGE\3\Machine\File.txt
set OutputFile=F:\EXCHANGE\3\File-New.txt
set CopyFile=F:\EXCHANGE\3\OldMachine\OldFile.txt
set _strFindPword=Pword=.*
for /F "delims=" %%A in ('findstr /x "Pword=.*" %CopyFile%') do set _strInsertPword=%%A
echo.%_strInsertPword%
set _strFindAccount=Account=.*
for /F "delims=" %%B in ('findstr /x "Account=.*" %CopyFile%') do set _strInsertAccount=%%B
echo.%_strInsertAccount%
set _strFindTown=Town=.*
for /F "delims=" %%C in ('findstr /x "Town=.*" %CopyFile%') do set _strInsertTown=%%C
echo.%_strInsertTown%
set _strFindLocationChanged=LocationChanged=.*
for /F "delims=" %%D in ('findstr /x "LocationChanged=.*" %CopyFile%') do set _strInsertLocationChanged=%%D
echo.%_strInsertLocationChanged%
set _strFindPostcode=Postcode=.*
for /F "delims=" %%E in ('findstr /x "Postcode=.*" %CopyFile%') do set _strInsertPostcode=%%E
echo.%_strInsertPostcode%
(
for /F "delims=" %%L in ('findstr /n "^" "%InputFile%"') do (
set "line=%%L"
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if "%%L" equ "_strFindPword" (echo.!_strInsertPword!) else (
if "%%L" equ "%_strFindAccount%" (echo.!_strInsertAccount!) else (
if "%%L" equ "%_strFindTown%" (echo.!_strInsertTown!) else (
if "%%L" equ "%_strFindLocationChanged%" (echo.!_strInsertLocationChanged!) else (
if "%%L" equ "%_strFindPostcode%" (echo.!_strInsertPostcode!) else (echo.!line!)
)
)
)
)
endlocal
)
) > "%OutputFile%"
del %InputFile%
ren %OutputFile% File.txt
pause
I think I finally got it...
What it does:
It goes through the OldFile.txt content, searching for markers, if found they are stored into environment variables to be used in the nest step (e.g. for _PWD marker (variable) which has a value of Pword=, it will create a _PWDCONTENTS variable with the content of Pword=ABC).
It goes through File.txt content, searching for the same markers, if one marker found, the corresponding CONTENTS variable is dumped in the OutFile.txt, else the original line. Because that happens in the inner for loop, I had to add some extra logic (the _WROTE var) to avoid writing the same lines more than once.
Notes:
It is supposed (well, besides doing what it's supposed to) to be "configurable" (the code is complicated, it's heading towards meta :) if you will), meaning that if there are changes between the markers the code shouldn't change (well there would be code changes, but not in the functional part only in variable definitions). Let me detail:
If you no longer need to replace the Town= string, then all you have to do is removing _TOWN from _ALL: set _ALL=_PWD _ACCT _POST _LOC.
The reverse: if you want to add some other tag (let's call it Name), you have to create a new environment variable: set _NAME=Name= and add it to _ALL: set _ALL=_PWD _ACCT _TOWN _POST _LOC _NAME.
As an indirect consequence, I didn't focus on performance, so it might run slow. Anyway I tried to keep the disk accesses (which are painfully slow) to a minimum (one example is when having 2 for loops the one that iterates on a file contents - assuming that each iteration takes a disk access; this might not be true, and Win has IO buffering - it's the outer one).
I "commented" out the last line in the file, to avoid overwriting the original file. If that behavior is needed, simply remove the rem at the beginning.
Here's the batch code:
#echo off
setlocal enabledelayedexpansion
set _INFILE="File.txt"
set _OUTFILE="NewFile.txt"
set _OLDFILE="OldFile.txt"
set _PWD=Pword=
set _ACCT=Account=
set _TOWN=Town=
set _POST=Postcode=
set _LOC=LocationChanged=
set _ALL=_PWD _ACCT _TOWN _POST _LOC
echo Parsing old file contents...
for /f "tokens=*" %%f in ('type !_OLDFILE!') do (
for %%g in (!_ALL!) do (
echo %%f | findstr /b /c:!%%g! 1>nul
if "!errorlevel!" equ "0" (
set %%gCONTENTS=%%f
)
)
)
copy nul %_OUTFILE%
echo Merging the old file contents into the new file...
set _WROTE=0
for /f "tokens=*" %%f in ('findstr /n "^^" !_INFILE!') do (
set _TMPVAR0=%%f
set _TMPVAR0=!_TMPVAR0:*:=!
for %%g in (!_ALL!) do (
echo !_TMPVAR0! | findstr /b /c:!%%g! 1>nul
if "!errorlevel!" equ "0" (
echo.!%%gCONTENTS!>>!_OUTFILE!
set _WROTE=1
)
)
if "!_WROTE!" equ "0" (
echo.!_TMPVAR0!>>!_OUTFILE!
) else (
set _WROTE=0
)
)
rem copy /-y %_OUTFILE% %_INFILE%
#EDIT0: Using #StevoStephenson suggestion (as part of the question snippet), I replaced the (2nd) outer for loop to ('findstr /n "^^" !_INFILE!') in order to include the empty lines, so the 3rd remark no longer applies (deleting). Also did some small changes to allow files that contain SPACE s in their paths.
Maybe it works like this
set CopyFile=oldfile.txt
set InputFile=newfile.txt
set str_search="Pword"
for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i
set str_replace="%str_replace%"
echo %str_search%
echo %str_replace%
pause
CALL :far %InputFile% %str_search% %str_replace%
EXIT /B 0
:far
setlocal enableextensions disabledelayedexpansion
set "search=%2"
set "replace=%3"
::remove quotes
set search=%search:"=%
set replace=%replace:"=%
echo %search%
echo %replace%
set "textFile=%1"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
EXIT /B 0
At for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i you write the line with the variable that has the needed info to str_replace.
After that you the program calls an embeded find-and-replace-function (:far) whitch i shemelessly stole from Batch script to find and replace a string in text file without creating an extra output file for storing the modified file
This function finds the string "Pword" and replaces it by the line find in the old file.
Attention:
This doesn't solve your problem completely since your new file has to be s.th like this.
#Password
Pword
so if you loose the = it works otherwise it doesn't. I hope this helps you with your problem.
It's not perfect but this may be okay for you:
#Echo Off
Setlocal EnableExtensions DisableDelayedExpansion
(Set InputFile=F:\EXCHANGE\3\Machine\File.txt)
(Set OutputFile=F:\EXCHANGE\3\File-New.txt)
(Set CopyFile=F:\EXCHANGE\3\OldMachine\OldFile.txt)
For /F "Delims=" %%I In (
'FindStr/B "Pword= Account= Town= LocationChanged= Postcode=" "%CopyFile%"'
) Do Set %%I
(For /F "Tokens=1-2* Delims=]=" %%I In ('Find /V /N ""^<"%InputFile%"') Do (
Echo(%%J|FindStr/B # || (If Defined %%J (Call Echo=%%J=%%%%J%%) Else (
If "%%J" NEq "" (Echo=%%J=%%K) Else (Echo=)))))>%OutputFile%
Timeout -1
EndLocal
Exit/B
I've left the delete and rename for you to add at the end.
This solution should be much faster than the other solutions.
It will also preserve empty lines and lines containing ! and ^.
It only needs one findstr call for collecting the old values for all words.
A second findstr determines all lines (by line number) in the infile which needs an update.
#echo off
setlocal EnableDelayedExpansion
set "_INFILE=File.txt"
set "_OUTFILE=NewFile.txt"
set "_OLDFILE="OldFile.txt"
set "_WORDS=Pword= Account= Town= Postcode= LocationChanged="
REM *** get all values for the key words
for /F "tokens=1,* delims==" %%L in ('findstr "!_WORDS!" "!_OLDFILE!"') do (
for /F %%S in ("%%L") do (
set "word[%%S]=%%M"
)
)
REM *** Find all lines which needs an update
set wordIdx=0
for /F "tokens=1,2,* delims=:= " %%1 in ('findstr /n "!_WORDS!" "!_INFILE!"') do (
set "lines[!wordIdx!].line=%%1"
set "lines[!wordIdx!].word=%%2"
set "replace=!word[%%2]!"
set "lines[!wordIdx!].replace=!replace!"
set /a wordIdx+=1
)
REM *** copy the infile to the outfile
REM *** Replace only the lines which are marked by line numbers
echo Parsing old file contents...
set nextWordIdx=0
set /a searchLine=lines[!nextWordIdx!].line
set lineNo=0
setlocal DisableDelayedExpansion
(
for /f "tokens=*" %%L in ('findstr /n "^" "%_INFILE%"') do (
set "line=%%L"
set /a lineNo+=1
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if !lineNo! equ !searchLine! (
(echo(!line!!lines[0].replace!)
set /a nextWordIdx+=1
for /F %%R in ("!nextWordIdx!") do (
endlocal
set /a nextWordIdx=%%R
set /a searchLine=lines[%%R].line
)
) ELSE (
(echo(!line!)
endlocal
)
)
) > "!_OUTFILE!"

Resources