-1

Good afternoon guys, I'm having a lot of difficulties trying to modify two values ​​inside an exe.conf file, the values ​​are <add key="FileName" value="20220623.txt"/>, I need to modify the value of value.

The truth is that I am not very aware of the batch language, but if you give me a hand I will be very grateful.

I tried to do it with this line, and some variants but I couldn't modify it.

setlocal enableDelayedExpansion
set "newValue='20221008.txt'"
type "test.exe.conf"|findstr  /v "(<add key="row_process" value="$!newValue!"/>) >fileName.conf.new
move /y "fileName.conf.new" "fileName.conf"
aynber
  • 22,380
  • 8
  • 50
  • 63
  • What you posted as code was not a line, it was several. I have therefore added line breaks where they should have been. – Compo Aug 10 '22 at 17:01
  • Do you _really_ need to use a batch file, or can you use Microsoft's PowerShell instead? – Charles Duffy Aug 11 '22 at 20:34

3 Answers3

1

In windows, you can use MSXML and JavaScript from cscript.exe

You'd run the following command

cscript xmlbatch.wsf file.xml "new value"

Here's a sample xmlbatch.wsf script, written in JavaScript.

<package>
<job id="t1">
<script language="JScript">

    var fso = new ActiveXObject( "Scripting.FileSystemObject" );
    var objArgs = WScript.Arguments;

    var strDOMObject = "MSXML2.FreeThreadedDOMDocument";
    var xml = new ActiveXObject( this.strDOMObject );
    
    if( objArgs.length < 2 ) {
        WScript.Echo( "Usage: cscript xmlbatch.wsf file.xml value");
        WScript.Echo( "Outputs the value(s) of all matching nodes" );
        WScript.Quit( 1 );
    }
    var strFileName = objArgs(0);
    var strValue = objArgs(1);

    var strXPath = "//add[@key='FileName']";

    if (fso.FileExists(strFileName) == false) {
        WScript.Echo( "Cannot locate " + strFileName );
        WScript.Quit( 1 );
    }

    try {
        if( !xml.load( strFileName ) ) {
            // output any errors, for invalid XML
            var strErrMsg = '';
            strErrMsg = xml.parseError.reason;
            if( xml.parseError.srcText != "" )
                strErrMsg += "Source: " + xml.parseError.srcText + "\r\n";
            if( xml.parseError.line != 0 )
                strErrMsg += "Line: " + xml.parseError.line + "\r\n";
            if( xml.parseError.linepos != 0 )
                strErrMsg += "Position: " + xml.parseError.linepos + "\r\n";
            throw new Error( xml.parseError.errorCode, strErrMsg ); 
        }

        var nodeList = xml.selectNodes( strXPath );
        if( nodeList != null ) {
            for( var i = 0; i < nodeList.length; i++ ) {
                nodeList[i].setAttribute( "value", strValue );
            }
            xml.save( strFileName );
        } else {
            WScript.Echo( "No matching nodes found in " + strFileName + " with XPath \"" + strXPath + "\"" );
            WScript.Quit( 1 );
        }
    
        WScript.Quit( 0 ); // success
    } catch( e) {
        WScript.Echo( e.description );
    }

</script> 
</job>
</package>

You may need to install MSXML if your machine doesn't have it already.

William Walseth
  • 2,803
  • 1
  • 23
  • 25
  • Good morning! First of all, thank you very much for the answer, maybe I missed giving more information about the problem I have, that data "value" is so that it takes the logs that are in another folder, I am making a bat so that I can indicate that log ( year/month/day) I need to take, in itself it is to leave the file intact but only modify that field with the date that I require since later a task is executed that calls that file "exe.conf" with the date that I want indicate. I hope I was clear, if not please let me know. Thank you as well! – Giovanni Morales Aug 11 '22 at 14:31
  • You may consider calculating the new date, and possibly making the new folder in the xmlbatch.wsf too. Good luck with your project. – William Walseth Aug 11 '22 at 17:10
  • Using a language with proper XML parsing tools makes this very much the Right Way to address this problem. – Charles Duffy Aug 11 '22 at 20:35
0

It seems to me that this is an "XY" problem in SO parlance, that is the problem presented is a faulty solution to an unstated problem.

The fundamental problem appears to be: Starting with a template file test.exe.conf which contains a line <add key="FileName" value="20220623.txt"/>, produce a new file having substituted a new value for 20220623.

OP's code is designed to remove the <add key.. line, and there's nothing in that code to make a substitution.

Consequently, I'll present a solution aimed at curing the problem, not the code.

Since I'm making some assumptions about the problem, it may not be quite what is desired.

The first issue is what string to substitute for 22020623 - I'd conclude it's likely to change over time, so I've added a few line to enable that data to be supplied or entered instead of changing the batch file. This means that you could execute thisbatchname 20221008 to run the batch and produce a file with 20221008 substituted for 22020623, or execute thisbatchname and the batch will prompt for the string to substitute for 22020623. Be warned though that whatever string is entered in either case will be literally substituted.

The second issue is that I'm assuming that test.exe.conf is a template file, so it can be manipulated - hence, take the original test.exe.conf and modify it so that each <add key... line is replaced by a unique string. I chose substitute line here - but it could be almost any string - @#123 for instance.

@ECHO OFF
SETLOCAL
rem The following settings for the source directory, destination directory,
rem filename, and output filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.

SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
SET "filename1=%sourcedir%\q73308290.txt"
SET "outfile=%destdir%\outfile.txt"

:: Get `newvalue`
SET "newvalue=%1"
IF NOT DEFINED newvalue SET /p "newvalue=New value ? "
IF NOT DEFINED newvalue GOTO :eof

(
FOR /f "usebackqdelims=" %%b IN ("%filename1%") DO (
 IF /i "%%b"=="substitute line here" (
  ECHO                     ^<add key="FileName" value="%newvalue%.txt"/^>
 ) ELSE ECHO %%b
)
)>"%outfile%"

GOTO :EOF

Always verify against a test directory before applying to real data.

Note that if the filename does not contain separators like spaces, then both usebackq and the quotes around %filename1% can be omitted.

The filenames shown suit my system and allow me to isolate the unique data for a question.

The file is assigned line-by-line to %%b. If %%b is not the trigger line substitute line here then it's simply reproduced by the echo %%b, but if it is a trigger line, then the required substitute line is generated instead.

All output from the echos is gathered into a new file outfile.txt by means of surrounding the code causing the echoes with a pair of parentheses and >filename.

--- revision after comments

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION 
rem The following settings for the source directory, destination directory,
rem filename, and output filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.

SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
SET "filename1=%sourcedir%\q73308290.txt"
SET "outfile=%destdir%\outfile.txt"

:: Get `newvalue`
SET "newvalue=%1"
IF NOT DEFINED newvalue SET /p "newvalue=New value ? "
IF NOT DEFINED newvalue GOTO :eof

FOR /f "tokens=5delims==/ " %%e IN ('FINDSTR /i /R /c:".*add key=\"FileName\" value=\"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].txt\"/." "%filename1%"') DO SET "oldfn=%%~e"&GOTO process

:process
(
FOR /f "usebackqdelims=" %%b IN ("%filename1%") DO (
 ECHO "%%b"|FINDSTR /i /R /c:".*add key=\"FileName\" value=\"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].txt\"/."  >nul
 IF ERRORLEVEL 1 (
  ECHO %%b
 ) ELSE (
  SET "line=%%b"
  echo !line:%oldfn%=%newvalue%.txt!
 )
)
)>"%outfile%"

:done
FC /w "%outfile%" "%filename1%"


GOTO :eof

Always verify against a test directory before applying to real data.

Note that if the filename does not contain separators like spaces, then both usebackq and the quotes around %filename1% can be omitted.

This time, %%e acquires the ????????.txt string from the line in question and assigns it to oldfn.

The file is then processed, simply regurgitating lines that do not contain the required string, otherwise substituting the new value for the old filename, using DELAYEDEXPANSION

... although a solution using SED would probably be cleaner if 3rd party utilities are allowed.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • Good morning! First of all, thank you very much for the answer, maybe I missed giving more information about the problem I have, that data "value" is so that it takes the logs that are in another folder, I am making a bat so that I can indicate that log ( year/month/day) I need to take, in itself it is to leave the file intact but only modify that field with the date that I require since later a task is executed that calls that file "exe.conf" with the date that I want indicate. I hope I was clear, if not please let me know. Thank you as well! – Giovanni Morales Aug 11 '22 at 14:31
0

Batch can't officially parse/edit XML, so it's highly recommended to use an XML-parser like , or instead.

xmlstarlet.exe ed -O -u "//add[@key='FileName']/@value" -v "20221008.txt" "fileName.conf"
<add key="FileName" value="20221008.txt"/>
xidel.exe -s "fileName.conf" -e "x:replace-nodes(//add[@key='FileName']/@value,function($x){attribute {name($x)} {'20221008.txt'}})" --output-node-format=xml
<add key="FileName" value="20221008.txt"/>
Reino
  • 3,203
  • 1
  • 13
  • 21