'Batch Replace and add 1 to a number between specific text in an xml file
Ok i'm trying to write a batch to "grab" a serial number for the user. The serial number comes from another system and we have to periodically manually input a serial number. I would like the user to run the batch file which would add 1 and replace the old number and then echo "Your serial number is ##" where ## is the new number. I'm pretty sure i can work out the Echo part and the writing to a temp file and overwriting the old file but its the middle part that im struggling with. The xml file is that is being edited is:
<?xml version="1.0"?>
<BatchSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
<LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
<LastSerialNumber>10</LastSerialNumber>
</BatchSettings>
the number i need to change is the ## between "lastserialnumber" <LastSerialNumber>10</LastSerialNumber>.
I've tried a few different approaches and this first one is close but definitely no cigar.
@echo off
set /a "replace=10"
set /a "replaced=replace+1"
set "source=Test.txt"
set "target=Test2.txt"
setlocal enableDelayedExpansion
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
set "line=%%b"
if defined line set "line=!line:%replace%=%replaced%!"
echo(!line!
)
) > %target%
endlocal
The problem with that one is the number won't always be "10" and I also don't want to replace all the other instances of "10" in the file.
I've also tried
@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"
@echo off
setlocal enableextensions disabledelayedexpansion
(for /f "delims=" %%a in (%SrcFile%
) do for /f "tokens=2 delims=1234567890 " %%b in ("%%~a"
) do if not "%%b"==":" (
echo(%%~a
) else for /f "tokens=1 delims= <LastSerialNumber>:" %%c in ("%%~a"
) do (
set /a "n=%%c+1"
setlocal enabledelayedexpansion
echo(<LastSerialNumber>!n!:
endlocal
)) > %OutFile%
but that doesn't increment the number and cuts off the remaining text. and this one is just a hot mess that doesn't work at all.
@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"
setlocal EnableDelayedExpansion
(for /F "delims=" %%a in ('findstr /I /L "<LastSerialNumber>" %SrcFile%') do (
set "line=%%a
set "line=!line:*<LastSerialNumber>=!"
for /F "delims=<" %%b in ("!line!") do echo %%b+1
)) > %oldnumber%
set "newnumber=%oldnumber%+1"
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %SrcFile%') do (
set "line=%%b"
if defined line set "line=!line:%oldnumber%=%newnumber%!"
echo(!line!
)
) > %OutFile%
endlocal
Solution 1:[1]
Okay, i've finally got the code to work how i need. Just have to set directories which i've left off here for obvious reasons. Thank you very much to aschipfl for the help. This code indexes the serial number by one, writes to temp file, then writes back and deletes temp file. Then displays the new serial number.
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=BatchTest.xml" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)
rem // Loop over all (non-empty) lines of the input file:
(for /F "usebackq delims=" %%L in ("%_FILE%") do (
rem // Store current line string:
set "LINE=%%L"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Split off opening tag from line string:
set "TEST=!LINE:*%_TAG%=!"
rem // Check whether opening tag has been found:
if not "!TEST!"=="!LINE!" (
rem // Opening tag found, hence split off closing tag:
for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
rem // Get extracted number and increment it:
set /A "NUM=%%S+1"
rem // Return rebuild line with incremented number:
echo( !_TAG!!NUM!^<%%T
)
) else (
rem // Opening tag not found, hence return original line:
echo(!LINE!
)
endlocal
))>BatchTestTmp.xml
copy /v /y "BatchTestTmp.xml" "BatchTest.xml" & cls
del "BatchTestTmp.xml"
set "LINE=4"
(for /f "usebackq delims=" %%a in (`more +%LINE% BatchTest.xml`) DO (
set result=%%a
setlocal EnableDelayedExpansion
echo Your Serial Number is !result:~20,2!
pause
goto :leave
))
:leave
endlocal
exit /B
Solution 2:[2]
As impressive as it's to see that good old Batch can still solve this issue, it's not designed with support for XML in mind. So I would recommend a tool that natively supports XML, like xidel.
The "ECHO part" is easy:
xidel -s input.xml -e "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1, is the new number')"
xidel -s input.xml -e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'"
Your serial number is 10 where 11 is the new number
The editing:
xidel -s input.xml -e "x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1})" --output-format=xml --output-declaration="<?xml version=\"1.0\"?>"
<?xml version="1.0"?>
<BatchSettings xsi="http://www.w3.org/2001/XMLSchema-instance" xsd="http://www.w3.org/2001/XMLSchema">
<SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
<LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
<LastSerialNumber>11</LastSerialNumber>
</BatchSettings>
To replace the input-file, simply add --in-place:
xidel -s --in-place input.xml ...
To write the output to another file, you could of course redirect the output to one...
xidel -s input.xml ... > input_edit.xml
...or you can use file:write():
xidel -s input.xml -e "file:write('input_edit.xml',x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}),{'omit-xml-declaration':false()})"
However, file:write() in this case puts out <?xml version="1.0" encoding="UTF-8"?>. If you want the original declaration (without the "encoding" bit), then you have to use file:write-text() and add it manually. This combined with the "ECHO part":
xidel -s input.xml -e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'" -e "file:write-text('input_edit.xml',('<?xml version=\"1.0\"?>',serialize(x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}))))"
Your serial number is 10 where 11 is the new number
The first extraction query prints the message string. The second writes the output to 'input_edit.txt'.
The last Xidel command prettified (with the necessary escape-characters):
xidel -s input.xml ^
-e "x'Your serial number is {//LastSerialNumber} where {//LastSerialNumber + 1} is the new number'" ^
-e ^"^
file:write-text(^
'input_edit.xml',^
(^
'^<?xml version=\"1.0\"?^>',^
serialize(x:replace-nodes(//LastSerialNumber/text(),function($x){$x + 1}))^
)^
)^
"
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Bryan Sonnier |
| Solution 2 |
