2

I'm trying to create a script for modifying a xml file. In this xml file, I have some lines like this:

<tag parameter="value" versionNumber="2.0.3" anotherParameter="value">

and I want to replace the value of the versionNumber with a new one.

I don't have any problem about going through the different lines, but I'm struggling with the value change. Any ideas? I would not use any additional scripts or tools, just batch.

Thanks in advance

m_jero
  • 147
  • 11
  • 2
    The question title, body, and tags appear to be in conflict. The title specifies "bash" which I doubt is correct since "batch" is in the question body and the tags "batch-file", and "windows" are applied. I suspect the title should read something like "Replace XML attribute value using a batch file". – kbulgrien Apr 28 '16 at 14:37
  • You are totally right. I've been working during the last 4 years just in Linux and it is still hard to swap – m_jero Apr 28 '16 at 14:43
  • XML files are difficult to handle with pure batch scripting; I recommend to use a language that supports XML rather than interpreting the data as "normal" text, which is the only possibility in batch... anyway, what have you tried so far? – aschipfl Apr 28 '16 at 17:21

3 Answers3

2

Here is a pure solution (let us call it change-version.bat):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem Define constants here:
set "XML_FILE=.\sample.xml"
set "TAG_NAME=tag"
set "PAR_NAME=versionNumber"
set "PAR_NEW_VAL="3.2.1""

for /F usebackq^ delims^=^ eol^= %%L in ("%XML_FILE%") do (
    set "LINE=%%L"
    for /F "tokens=1,* delims=< eol=<" %%D in ("%%L") do (
        set "PRE=%%D" & set "LIN=%%E"
        if not defined LIN (set "PRE=" & set "LIN=%%D")
        setlocal EnableDelayedExpansion
        for /F "tokens=1,* eol= " %%F in ("!LIN!") do (
            setlocal DisableDelayedExpansion
            set "INT=%%F" & set "TAG=%%G"
            setlocal EnableDelayedExpansion
            if /I "!INT!"=="%TAG_NAME%" (
                for /F "tokens=1,* delims=> eol=>" %%T in ("!TAG!") do (
                    setlocal DisableDelayedExpansion
                    set "TAG=%%T" & set "END=%%U"
                    call :SUB NEW TAG
                    setlocal EnableDelayedExpansion
                    echo(!PRE!^<%TAG_NAME%!NEW!^>!END!
                    endlocal
                    endlocal
                )
            ) else (
                echo(!LINE!
            )
            endlocal
            endlocal
        )
        endlocal
    )
)

endlocal
exit /B


:SUB var_new var_tag
set "%~1="
:LOOP
setlocal EnableDelayedExpansion
for /F "tokens=1,* eol= " %%P in ("!%~2!") do (
    endlocal
    for /F "tokens=1,* delims== eol==" %%V in ("%%P") do (
        if /I "%%V"=="%PAR_NAME%" (
            set "PAR=%%V=%PAR_NEW_VAL%"
        ) else (
            set "PAR=%%V=%%W"
        )
    )
    set "%~2=%%Q"
    setlocal EnableDelayedExpansion
)
for /F delims^=^ eol^= %%S in ("!%~1! !PAR!") do (
    endlocal & set "%~1=%%S"
)
if defined %~2 goto :LOOP
exit /B

For this to work, the following conditions for the XML data must be fulfilled:

  • the line that holds the tag <tag> does not contain any other XML tags;
  • the tag <tag> containing the parameter versionNumber must not be spanned over multiple lines;
  • a parameter definition must not contain white-spaces around the = sign;
  • the (quoted) parameter values must not contain < nor > characters;
  • both tag and parameter names are treated case-insensitively;

The script parses the XML file sample.xml in the current directory.
To write the result to a file rather than to the console, redirect it like this:

"change-version.bat" > "result.xml"
aschipfl
  • 33,626
  • 12
  • 54
  • 99
1

With onboard stuff that ain't too easy. Check Is there any sed like utility for cmd.exe

Basically you will create a short script that will perform replacements (sed.vbs):

Dim pat, pa tparts, rxp, inp
pat = WScript.Arguments(0)
patparts = Split(pat,"/")
Set rxp = new RegExp
rxp.Global = True
rxp.Multiline = False
rxp.Pattern = patparts(1)
Do While Not WScript.StdIn.AtEndOfStream
    inp = WScript.StdIn.ReadLine()
    WScript.Echo rxp.Replace(inp, patparts(2))
Loop

and finally execute the command to replace from the Windows Batch:

csript //NoLogo sed.vbs s/2.0.3/2.0.4/ < yourFile.xml > newFile.xml
Community
  • 1
  • 1
0

There are several different ways to solve this problem, but the most efficient one is via the regexp replace feature of VBScript/JScript programming languages, and the simplest way to do so is with a Batch-JScript hybrid script:

@set @a=0  /*
@cscript //nologo //E:JScript "%~F0" < input.xml > output.xml
@goto :EOF */


// JScript code section

WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/versionNumber=[^ ]+ /g,"versionNumber=\"X.Y.Z\" "));

Save this file with .BAT extension.

Aacini
  • 65,180
  • 12
  • 72
  • 108