使用批处理文件编辑 XML
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1946717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Edit XML with batch file
提问by Christopher B. Adkins
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:
我想知道是否有任何方法可以创建可以编辑 XML 文档中一行的批处理文件。该行将由前一行标识。想法如下:
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.
这样的事情甚至可能发生还是我在做梦超出我的能力?感谢您的帮助和答案。
回答by Joey
You probably couldhack 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.
您可能可以在一个以某种方式工作的批处理文件中组合一些东西。但这会异常痛苦。首先,我知道没有办法可靠地将行读入批处理文件中的变量并将它们写回原封不动的文件。您可以转义大多数有问题的字符(例如<, >, &, |, ...),但仍然存在我无法解决的问题1(例如不匹配的引号)会导致此类尝试严重失败。然后您仍然无法解析 XML,但您更愿意进行原始文本处理,一旦使用单引号而不是双引号,这可能很容易失败。或者在某个地方扔了一个额外的空间。或者您要查找的行被分成几行。所有有效的 XML 但在没有 XML 解析器时解析起来很痛苦。
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).
批处理文件语言并不真正适合此类任务。哎呀,它几乎不适用于文本处理,但 XML 远不止于此。使用 VBScript 和 MSXML 甚至 PowerShell(如果适用),您可能会更幸运(和乐趣)。
VBScript is probably the most sane choice here as you can rely on it existing on virtually any modern Windows machine.
VBScript 可能是这里最明智的选择,因为您可以依赖它存在于几乎任何现代 Windows 机器上。
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).
您还可以使用 XSLT 并从命令行调用它。有足够多的 XSLT 处理器可以使用,生成 XSLT 文件实际上要简单得多(但仍需要多次转义)。
1Note 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.
1请注意,我可能是高级批处理文件用户/程序员,但绝不是权威。也许这很容易实现,我只是太愚蠢了看不到它。
回答by rud3y
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.
我实际上对此有一个答案。是的,这很痛苦,但是我遇到了类似的问题,并且我实际上并不了解 VBScript(尽管我打算学习它......)虽然我的问题发生在一位同事的客户有 20,000 个文件中外部数据的转换。所有文件都是 xml,并且它们都缺少 XML 的相同第二行,这触发了我们正在导入的文档的重新文件。
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)...
我写了一个标准的批处理脚本和我在 StackOverflow 上找到的另一个脚本,它允许我将文件分成 2 部分,然后在它们之间插入我想要的代码。现在我唯一的问题(可能是由于懒惰或我缺乏知识/耐心)是我无法摆脱 < , > 问题。脚本一直认为我正在尝试写入无效的文件。我尝试了各种方法来使用该字符,但我希望以可变形式使用它。不用说,我让它工作(甚至)......
Below is the readme I provided to my coworker, along with the code from each file.
下面是我提供给我同事的自述文件,以及每个文件中的代码。
README.txtProblem: Massive amount of files were missing a string or piece of code and need to be edited
README.txt问题:大量文件缺少一个字符串或一段代码,需要编辑
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.
此工具共有 4 个文件。
**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:
你需要做什么:
Edit String_Insert_Launcher and place this file in the directory with the files you want to edit. NOTEIt 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
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
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
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.
编辑 String_Insert_Launcher 并将此文件放在要编辑的文件所在的目录中。注意此文件必须与您要编辑的所有其余文件位于同一文件夹中。您需要编辑此文件中的变量以匹配您的文件系统批处理路径
编辑 InsertString.bat 并将这个文件放在你在上面设置 batchpath 变量的同一个目录你需要编辑这个文件中的变量以匹配你的文件系统 insertpath destpath top_last_line insert_last_line bot_last_line
编辑 insert.txt 并将此文件放在与上面设置的插入路径相同的目录中 您需要将要插入到文件中的字符串放在此文本文档中
检查您的日志并确保“Modified_Filelist.txt”(在 %insertpath% 中找到)中的文件数与您开始使用的文件数相同。
Breakdown of files:
文件分解:
* insert.txt *
* 插入.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 中设置的名为“insertpath”或在批处理文件中作为 %insertpath% 引用的变量位于同一位置。
* InsertString.bat *
* 插入字符串.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 中设置的变量位于同一位置,称为“batchpath”,或在批处理文件中作为 %batchpath% 引用。
* String_Insert_Launcher.bat *
* 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.
这是您将执行以编辑所有文件的脚本。从包含要编辑的文件的文件夹启动此批处理脚本。该文件获取所有文件名并在所有这些文件上运行 InsertString.bat。在此文件中,您将找到一个需要为脚本设置的变量。变量包括:
batchfilepath- 这是完成所有工作的实际批处理文件的位置。这个位置只是文件路径,不包括任何文件名。
FILE #1: String_Insert_Launcher.bat
文件 #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
文件 #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
文件 #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
在您的情况下,您可能可以使用 2 个文件...一个 with<value>一个 with </value>(我知道这很草率,但它会起作用...)然后从我的批处理脚本 InsertString.bat 中,您只需放置 :next 循环2x(每个文件一个),在它们之间放置 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.
就像我说的,我知道这很麻烦,您可以在 VBScript 中更轻松地做到这一点,但对于我们这些不知道它的人来说,这是一个有效的解决方案。
回答by Aacini
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:
通过批处理程序处理 XML 文件不仅简单直接,而且在我看来,比 VBScript、PowerShell 等中的任何等效解决方案都更容易。它是:
@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:
以前程序的唯一问题是它从 XML 文件中删除了空行,但是可以通过添加更多命令以非常简单的方式修复此细节。之前的程序可能会被修改为不检查完整的行(如 OP 最初要求的那样),而是以与上一个 VBScript 示例相同的方式检查三个部分:
(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
回答by ghostdog74
sure, natively, you can use batch, but i recommend you to learn and use vbscript instead
当然,在本地,您可以使用批处理,但我建议您学习并使用 vbscript
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
将脚本另存为 edit.vbs 并在您的批处理中
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.
如果您讨厌使用其他工具(如 gawk/sed/Python/Perl 或其他 xml 解析器/编写器)的想法,那么 vbscript 是您获得的最好的,除了 cripple batch 之外。否则,您应该考虑使用这些更好的工具。
回答by Jason S
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?)
XML 不是基于行的,因此假设您可以通过逐行检查来查找文件中的某些内容,这要么容易出现问题,要么依赖于除 XML 之外的其他假设。(如果您从某种类型的软件中获取文件,您怎么知道它总是会以这种特定方式生成输出行?)
Having said that, I'd take a look at JSDBJavascript, which has E4Xbuilt-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:
话虽如此,我还是看一下内置E4X 的JSDBJavascript 。E4X 使得操作 XML 变得特别简单,只要您可以将其全部读入内存;它不是基于流的系统。尽管您可以在没有 E4X 的情况下使用 JSDB 并使用流处理文件 I/O:
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();

