Edit XML with batch file

2020-03-04 06:08发布

I am wondering if there is any way to create a batch file that can edit a line in an XML document. The line would be identified by the preceding line. the idea would be as follows:

If line == Csetting name="BaseDirectory" serializeAs="String">
    Next line = <value>User Input from begining of batch</value>

is something like that even posible or am I dreaming outside of my means? Thanks for the help and the answers.

5条回答
来,给爷笑一个
2楼-- · 2020-03-04 06:44

You probably could hack something together in a batch file that works somehow. But it will be extraordinarily painful. First of all, I know of no way of reliably reading lines into variables in a batch file and writing them back to a file unaltered. You can escape most of the problematic characters (such as <, >, &, |, ...) but there still are problems I couldn't solve1 (such as unmatched quotation marks) that will cause such attempts to fail horribly. Then you still wouldn't be able to parse XML but you'd rather to primitive text processing which may easily fail as soon as maybe single quotes are used instead of double quotes. Or an extra space is thrown in somewhere. Or the line you're looking for is split into several lines. All valid XML but painful to parse when no XML parser is around.

The batch file language isn't really suited for such tasks. Heck, it barely works for text processing but XML is way beyond. You may have more luck (and fun) with using VBScript and MSXML or even PowerShell (if applicable).

VBScript is probably the most sane choice here as you can rely on it existing on virtually any modern Windows machine.

You could also use XSLT and call that from the command-line. There are enough XSLT processors out there that can be used and generating an XSLT file is actually much simpler (but will still require several escapings).


1 Note that I may be an advanced batch file user/programmer but by no means authoritative. Maybe it's easily possible and I'm just too stupid to see it.

查看更多
姐就是有狂的资本
3楼-- · 2020-03-04 06:55

I actually have an answer for this. Yes it is painful, however I had a similar problem and I don't actually know VBScript (though I am planning on learning it...) for the time though my problem occurred with a coworker having a customer with 20,000 files they flubbed from a conversion of outside data. All files were xml and they all were missing the same 2nd line of the XML which triggered a refile of the document we were importing.

I wrote a standard batch script in tandem with another I found on StackOverflow which allowed me to split the files into 2 parts and then in between them insert the code I wanted. Now my only problem (probably due to laziness or my lack of knowledge/patience) was that I couldn't escape the < , > problem. The script kept thinking that I was trying to write to a file, which was invalid. I tried all sorts of ways to use that character, but I wanted it in a variable form. Needless to say, I got it working (well even)...

Below is the readme I provided to my coworker, along with the code from each file.

README.txt Problem: Massive amount of files were missing a string or piece of code and need to be edited

Solution: This tools takes apart files and injects a string or piece of code and then put the files back together in another location.

There are a total of 4 files that come with this tool.

    **1 - _README.txt       - This file describes how to use the script
    **2 - insert.txt            - This file contains the text that will be inserted into the file you need edited.
    **3 - InsertString.bat      - This file contains the actual script that loops to restructure the file. Here you will find all the variables that need to be set to make this work.
    **4 - String_Insert_Launcher.bat    - This file is what you will launch to run the InsertString.bat file.

What you need to do:

  1. Edit String_Insert_Launcher and place this file in the directory with the files you want to edit. NOTE It is imperative that this file be in the same folder as ALL of the rest of your files you want edited. You need to edit the variables in this file to match you filesystem batchpath

  2. Edit InsertString.bat and place this file in the same directory you set the batchpath variable above You need to edit the variables in this file to match your filesystem insertpath destpath top_last_line insert_last_line bot_last_line

  3. Edit the insert.txt and place this file in the same directory you set the insertpath above You need to put the string(s) you want to be inserted into your file inside this text document

  4. Check your logs and make sure that the number of files in the " Modified_Filelist.txt " (found in the %insertpath%) is the same as the number of file you started with.

Breakdown of files:


* insert.txt *


Inside this file you will want to put the text that you want inserted into the files you will target. The reason for using a separate file is so that special characters (>,<,/,\,|,^,%,etc...) aren't treated like arguments within the batch file. This file HAS TO BE in the same location as the variable you will set in InsertString.bat called ' insertpath ' or referenced in the batch file as %insertpath%.


* InsertString.bat *


Inside this file you will find the variables that need to be set for the script to work. Variables included:

            **1. filelist - This sets the counter for counting how many files were edited *this should not be edited*
        **2. insertpath - This sets the path of insert.txt file containing the string you want to insert into the files that will be edited. If this location does not exist it will create it.
        **3. destpath - This sets the path for the location of the files after they're edited. If this location does not exist it will create it.
        **4. top_last_line - This sets the LAST GOOD LINE of the file that will be edited before the insert.txt is added. In essence this will split the file into 2 parts and add the contents of " insert.txt " into the middle of those 2 parts.
        **5. insert_last_line - This sets the number of lines to add to the file from insert.txt (i.e. if insert_last_line=2 then the top two lines will be added after top_last_line)
        **6. bot_last_line - This sets the last line of the original file (i.e. if there are 25 lines in the original file bot_last_line should be 25 - always over esitimate this, because if this number is less than the original not all lines will be rewritten to the new file)

This file HAS TO BE in the same location as the variable you will set in String_Insert_Launcher.bat called ' batchpath ' or referenced in the batch file as %batchpath%.


* String_Insert_Launcher.bat *


This is the script you will execute to edit all the files. Launch this batch script FROM the folder with the files in it you want to edit. This file grabs all of the file names and runs the InsertString.bat ON all of these files. Inside this file you will find a varaible that nees to be set for the script to work. Variable included: batchfilepath - This is the location of the actual batch file that does all of the work. This location is JUST the filepath, not including any filenames.

FILE #1: String_Insert_Launcher.bat

@ECHO off
TITLE Insert String to XML Script Launch File
COLOR 02

set batchfilepath=C:\JHA\Synergy\insertpath
REM This is the location of the actual batch file that does all of the work. This location is JUST the filepath, not including any filenames.
IF NOT exist  %batchfilepath% md %batchfilepath% 
IF NOT exist %batchfilepath%\InsertString.bat goto pause

:run
for /f "delims=" %%f in ('dir /b /a-d-h-s') do "%batchfilepath%\InsertString.bat" %%f
REM This command string gets the names of all of the files in the directory it's in and then runs the InsertString.bat file against every file individually.

:pause
cls
echo.The file InsertString.bat is not in the correct directory.
echo.Please put this file in the location listed below:
echo.
echo.-------------------------
echo.%batchfilepath%
echo.-------------------------
echo.
echo.When this file has been added press any key to continue running the script.
pause
goto run

REM Insert String to XML Script
REM Created by Trevor Giannetti
REM An unpublished work

FILE #2: Insert_String.bat

@ECHO off
TITLE Insert String to XML Script
COLOR 02
SETLOCAL enabledelayedexpansion

REM From Command Line:              for /f "delims=" %f in ('dir /b /a-d-h-s') do InsertString.bat %f

REM ---------------------------
REM   *** EDIT VARIABLES BELOW ***
REM ---------------------------

set insertpath=C:\JHA\Synergy\insertpath
REM This sets the path of insert.txt file containing the string you want to insert into the files that will be edited. If this location does not exist it will create it.
set destpath=C:\JHA\Synergy\destination
REM This sets the path for the location of the files after they're edited. If this location does not exist it will create it.
set top_last_line=1
REM This sets the LAST GOOD LINE of the file to be edited before the insert.txt is added. In essence this will split the file into 2 parts and add the contents of " insert.txt " into the middle of those 2 parts.
set insert_last_line=1
REM This sets the number of lines to add to the file from insert.txt (i.e. if insert_last_line=2 then the top two lines will be added after top_last_line)
set bot_last_line=25
REM This sets the last line of the original file (i.e. if there are 25 lines in the original file bot_last_line should be 25 - always over esitimate this, because if this number is less than the original not all lines will be rewritten to the new file)

REM ---------------------------
REM  *** DO NOT EDIT BELOW ***
REM ---------------------------

set filelist=0
REM This sets the counter for counting how many files were edited
IF '%1'=='' goto usage

IF NOT exist %insertpath% md %insertpath%
IF NOT exist %destpath% md %destpath%

:top_of_file
IF EXIST %destpath%\%1 set done=T
IF EXIST %destpath%\%1 goto exit
IF '%1'=='InsertString.bat' goto exit
IF '%1'=='insert.txt' goto exit
IF '%1'=='Modified_Filelist.txt' goto exit
IF '%1'=='String_Insert_Launcher.bat'  goto exit
set /a FirstLineNumber = 1
REM This is the first line in the file that you want edited
set /a LastLineNumber = %top_last_line%
REM This is the last line in the file that you want edited

SET /a counter=1

for /f "usebackq delims=" %%a in (%1) do (
    if !counter! GTR !LastLineNumber! goto next
    if !counter! GEQ !FirstLineNumber! echo %%a >>  %destpath%\%1
    set /a counter+=1
)

goto next

:next
REM echo TEXT TO BE INSERTED >> %destpath%\%1
REM goto bottom_of_file
REM The above can be substituted for the rest of :next if you don't have special characters in the text you need inserted

set /a FirstLineNumber = 1
REM This is the first line in the file with the text you need inserted in the file you want edited
set /a LastLineNumber = %insert_last_line%
REM This is the last line in the file with the text you need inserted in the file you want edited

SET /a counter=1
for /f "usebackq delims=" %%a in (%insertpath%\insert.txt) do (
    if !counter! GTR !LastLineNumber! goto next
    if !counter! GEQ !FirstLineNumber! echo %%a >>  %destpath%\%1
    set /a counter+=1
)
REM The %insertpath%\insert.txt is the name of the file with the text you want inserted into the file you want edited

goto bottom_of_file

:bottom_of_file
set /a FirstLineNumber = 1+%top_last_line%
REM This is the first line in the second part of the file with the text you need inserted in the file you want edited
set /a LastLineNumber = %bot_last_line%
REM This is the last line in the second part of the file with the text you need inserted in the file you want edited
REM The above is the split, after the top_of_file. The rest of the contents of the original file will be added after the text you want inserted is appended to the file

SET /a counter=1

for /f "usebackq delims=" %%a in (%1) do (
    if !counter! GTR !LastLineNumber! goto exit
    if !counter! GEQ !FirstLineNumber! echo %%a >>  %destpath%\%1
    set /a counter+=1
)

goto logging

:logging
IF NOT EXIST %insertpath%\Modified_Filelist.txt echo Modified File List: > %insertpath%\Modified_Filelist.txt
for /f "tokens=1 delims=[]" %%a in ('find /v /c "" ^< %insertpath%\Modified_Filelist.txt') do (
echo %%a - %1 >> %insertpath%\Modified_Filelist.txt
)

goto exit

:usage
cls
echo Usage: InsertString.bat FILENAME 
echo You are missing the file name in your string

:exit
IF '%done%'=='T' echo %1 Already exists in folder!
IF '%done%'=='T' echo Not modifying %1
IF '%done%'=='T' echo Moving on to next file...
IF EXIST %destpath%\InsertString.bat del %destpath%\InsertString.bat
IF EXIST %destpath%\insert.txt del %destpath%\insert.txt

REM Insert String to XML Script
REM Created by Trevor Giannetti
REM An unpublished work

FILE #3: Insert.txt

<Vocabulary="Conv">

In your case you might be able to use 2 files...one with <value> and one with </value> (I know this is sloppy, but it will work...) Then from my batch script InsertString.bat you would just put the :next loop 2x (one for each of your files) and in between them you would put echo.%userInputFromBeginningofBatch% >> File.xml

Like I said, I know this is messy and you can doe it a lot easier in VBScript, but for those of us that don't know it this is a solution that does work.

查看更多
\"骚年 ilove
4楼-- · 2020-03-04 06:56

XML isn't line-based, so an assumption that you can look for something in the file by checking it on a line-by-line basis, is either prone to problems, or relies on other assumptions besides XML. (if you are getting your file from a certain type of software, how do you know it is always going to produce output lines in that particular way?)

Having said that, I'd take a look at JSDB Javascript, which has E4X built-in. E4X makes it particularly simple to manipulate XML, as long as you can read it all into memory; it's not a stream-based system. Though you could use JSDB without E4X and handle file I/O using streams:

var Sin = new Stream('file://c:/tmp/testin.xml');
var Sout = new Stream('file://c:/tmp/testout.xml','w');
while (!Sin.eof)
{
   var Lin = Sin.readLine();
   var Lout = some_magic_function(Lin); // do your processing here
   Sout.writeLine(Lout);
}
Sin.close(); Sout.close();
查看更多
家丑人穷心不美
5楼-- · 2020-03-04 06:58

Excuse me. I apologize in advance for this post. I know this is a very old topic, but after read the answers here I couldn't resist the temptation to post this answer.

The processing of a XML file via a Batch program is not just straightforward and direct, but in my humble opinion, easier than any equivalent solution in VBScript, PowerShell, etc. Here it is:

@echo off
setlocal EnableDelayedExpansion
set "greater=>"
set targetLine=Csetting name="BaseDirectory" serializeAs="String"!greater!
echo Enter the new line to insert below target lines:
set /P nextLine=
setlocal DisableDelayedExpansion

(for /F "delims=" %%a in (document.xml) do (
   set "line=%%a"
   setlocal EnableDelayedExpansion
   echo !line!
   if "!line!" equ "!targetLine!" echo !nextLine!
   endlocal
)) > newDocument.xml

The only problem with previous program is that it delete empty lines from the XML file, but this detail may be fixed in a very easy way by adding a couple commands more. Previous program may be modified to not check the complete line (as the OP originally requested), but check three parts in the same way of the last VBScript example:

(for /F "delims=" %%a in (document.xml) do (
   set "line=%%a"
   setlocal EnableDelayedExpansion
   echo !line!
   set lineMatch=1
   if "!line:Csetting name=!" equ "!line!" set lineMatch=
   if "!line:BaseDirectoy=!" equ "!line!" set lineMatch=
   if "!line:serializeAs=!" equ "!line!" set lineMatch=
   if defined lineMatch echo !nextLine!
   endlocal
)) > newDocument.xml
查看更多
迷人小祖宗
6楼-- · 2020-03-04 07:06

sure, natively, you can use batch, but i recommend you to learn and use vbscript instead

Set objFS=CreateObject("Scripting.FileSystemObject")
strFile = WScript.Arguments.Item(0)
strUserValue= WScript.Arguments.Item(1)
Set objFile = objFS.OpenTextFile(strFile)
Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    If  InStr(strLine,"Csetting name") >0 And _
        InStr(strLine,"BaseDirectory")> 0 And _
        InStr(strLine,"serializeAs=") > 0 Then      
        strLine=strLine & vbCrLf & "<value>" & strUserValue & "</value>"        
    End If 
    WScript.Echo strLine
Loop

save the script as edit.vbs and in your batch

c:\test> cscript //nologo edit.vbs file "user value"

vbscript is the best you got besides cripple batch, if you hate the idea of using other tools like gawk/sed/Python/Perl or other xml parsers/writers. Otherwise, you should consider using these better tools.

查看更多
登录 后发表回答