I managed to write the following script (with the help of experts from Stack Overflow) to read the source batch script line by line, find the line with java.exe (first part) and a specific substring which is expected to be at the end of the line (last part), if found, update the line and insert the additional JVM switches before the last part. Then write the updated line to the output. If there is no match, the line is written to the output as is.
Below is the script which is working successfully:
@echo off
setlocal enabledelayedexpansion
set sourcefile=my_source_script.bat
:: Create the destination file
type NUL > new_%sourcefile%
:: Define the part to be inserted in the JVM Command Line
:: The switches to be inserted are defined in config.ini file
call :load_config
echo insert switches value = %INSET_JVM_SWITCHES%
REM Define the first and last part which will be used to identify the lines to be modified in the batch script
set "firstpart=java.exe"
set "lastpart=com.main.java.class.of.the.application -auto"
REM Read source line by line and prefix with line number to pick empty lines
FOR /f "delims=" %%l IN ('findstr /N "^" ".\%sourcefile%"') DO (
set "line=%%l"
REM Remove the line number until the column
set "line=!line:*:=!"
REM Initialize left and right parts. This is needed to identify the lines to be modified
set "left=x"
if not "!line!"=="" for %%b in ("!lastpart!") do set "left=x!line:%%~b=!"
if not "!line!"=="" for %%b in ("!firstpart!") do set "left=x!line:%%~b=!"
set "right=x!line!"
REM If firstpart and lastpart are found in line ...
if not "!line!"=="" if not !left!==!right! (
REM Update the line by replacing the last part with the addition plus the last part
for %%b in ("!lastpart!") do set "line=!line:%%~b=%INSET_JVM_SWITCHES% %lastpart%!"
)
REM Write the line to the new batch script
echo(!line!>>new_%sourcefile%
)
goto :eof
::**************************************************************************************
:: Function: Load Config
:: Purpose: Parse the text file config.ini
:load_config
echo.
echo Loading config data from config.ini file ...
echo.
SET "configfile=%currentpath%config.ini"
if not exist %configfile% (
echo Error: The following config file was not found:
echo %configfile%
exit /b 1
)
set "uniqueList="
:: remove variables starting count_
FOR /F "delims==" %%e In ('set count_ 2^>Nul') DO SET "%%e="
FOR /f "usebackq tokens=1* delims==" %%b IN ("%configfile%") DO (
IF "%%c" neq "" (
set keyname=%%b
IF DEFINED count_!keyname! (SET /a count_%%b+=1) ELSE (SET /a count_!keyname!=0 & set uniqueList=!uniqueList!,!keyname!)
if "!keyname:~-4!"=="_arr" (
SET "!keyname![!count_%%b!]=%%c"
) else (
SET "!keyname!=%%c"
)
)
)
:: Remove first char if it is a coma ,
if "!uniqueList!" neq "" if "!uniqueList:~0,1!" equ "," set uniqueList=!uniqueList:~1!
echo Completed parsing the config file.
echo.
goto :eof
::****************************************************************
In the end, I need to replace the source with the new file only after performing a check something along the following logic:
- The size of the new file is the same as the source file plus the number of inserted characters.
- The percentage of the difference between the source and the destination is small.
If the above criteria are matched, delete the source file and replace it with the new one.
How could this be done?
There is not defined the environment variable
currentpathas referenced in subroutineload_config. The lines of the fileconfig.iniare not posted too. I inserted for that reason after the first three lines of the subroutineload_configthe two lines:The lines of the file
my_source_script.batare also not posted in the question. I used for testing:That is of course no valid batch file. But it contains lines which are hard to handle correct using just Windows commands for making the file modification for which the Windows Command Processor is not designed at all. The usage of JScript or PowerShell would make the search and replace much easier and faster.
The last but one line of the example contents of
my_source_script.batshould be modified and that search and replace should be done only once even on user runs the batch file below multiple times.The batch file references the file
my_source_script.batand most likely alsoconfig.iniwith a path relative to current working directory. This can be any directory as the process startingcmd.exefor processing the batch file defines the current working directory orcmd.exechanges itself in case of the parent process defines a directory on a network resource using a UNC path as current working directory.Three examples for current working directory not being the directory containing the batch file:
%HOMEDRIVE%%HOMEPATH%as current working directory as defined in the shortcut fileCommand Prompt.lnkand runs the batch file with using the fully qualified file name"%USERPROFILE%\Downloads\UpdateBatch.cmd".The batch file user should at least get an error message output with fully qualified file name to see in which directory the batch file tried to update the batch file. That is the reason for using as third line:
There are added several additional conditions to inform the user of the batch file about various errors which could occur during the execution of the batch file preventing this batch file to update the other batch file. The user can see the error message on running the batch file from within a command prompt window, but not on double clicking the batch file as in this case
cmd.exeis started byexplorer.exewith the option/cand the batch file name appended resulting in an immediate close ofcmd.exeonce the batch file processing finished for whatever reason. There could be addedpause &left to everyexit /Bto halt the batch file execution until a key is pressed giving the user also the chance to see the error message on having started the batch file execution with a double click on the batch file in Windows File Explorer.The first added condition is:
The condition should be self-explaining.
The second added check is:
This command line prevents multiple updates of the same line if the user runs the batch file more than once for whatever reason.
The following line makes sure that the temporary file in the directory of the batch file to update does not already exist by chance:
It is of course possible that the batch file user does not have the permissions to modify any file in the directory of the batch file to update or there is very unlikely a directory with name of the temporary file.
The following condition handles at least the first unusual use case with write-protected directory:
The loop for creating the temporary file with updating the single line is rewritten for working also for a batch file containing one or more
!or a line beginning with;. For more details see: How to read and print contents of text file line by line? The loop is also with less command lines as before.There is determined next the length of the string with the JVM switches to insert to know the number of characters (=bytes) of which the updated batch file should grow in file size.
The file size of the source file and of the created temporary file with the updated line is determined and the file size difference is calculated using a 32-bit signed integer arithmetic. The batch file is surely less than 2 GiB (less than 2147483648 bytes) even after the insert of the JVM switches.
The source file is replaced by the temporary file with the updated line if the file size difference is equal the length of the inserted string (with the additional space character). The batch file processing is exited with exit code
0on this file replacement works as expected.Otherwise the batch file deletes the temporarily created file on existing at all and informs the user about the failed attempt to update the batch file before exiting with exit code
1indicating like2and3an error to the parent process.Read following pages for the reasons of the modifications in subroutine
load_config:The usage of
echo.for printing an empty line results always in searching bycmd.exefor a file with nameechoin the current working directory and in execution of this file on really existing.echo/andecho(results in the output of an empty line without accessing the file system at all.The comparison operators
EQUandNEQdesigned primary for comparing two integer values should not be used on comparing two strings enclosed in"although it is possible. The referenced answer explains the reasons.There is used
if %FileSizeDiff% == %ExpectedDiff%instead ofif "%FileSizeDiff%" == "%ExpectedDiff%"orif %FileSizeDiff% EQU %ExpectedDiff%because ofFileSizeDiffandExpectedDiffare always defined with the provided code with integer values as strings because of using an arithmetic expression for the final definition of each environment variable and a case-sensitive string comparison can be done by the CPU with less instructions to execute than an integer comparison with converting first both integer string values internally incmd.exeto 32-bit signed integers before doing the integer comparison.