如何使用Windows批处理提取特定XML标记属性的所有实例

时间:2012-06-21 15:18:01

标签: batch-file

我有一个XML文件,我需要提取

testname
来自

的所有实例

<con:testSuite name="testname" 

在XML文件中。

我不太清楚如何处理这个问题,或者这是否可以批量处理。

这是我到目前为止所想的:

1)使用FINDSTR并存储每行

<con:testSuite name=

在变量或临时文件中,如下所示:

FINDSTR /C:"<con:testSuite name=" file.xml > tests.txt

2)以某种方式使用该文件或变量来提取字符串

请注意,同一行中可能有多个匹配字符串实例。

我是批处理的新手,感谢任何帮助。

1 个答案:

答案 0 :(得分:4)

解析XML对批处理非常痛苦。 Batch不是一个好的文本处理器。但是,通过一些努力,您通常可以从给定的XML文件中提取所需的数据。但是输入文件可以很容易地重新排列成一个等同的有效XML格式,这将破坏你的解析器。

将该免责声明排除在外......

这是一个原生批处理解决方案

@echo off
setlocal disableDelayedExpansion
set input="test.xml"
set output="names.txt"

if exist %output% del %output%
for /f "delims=" %%A in ('findstr /n /c:"<con:testSuite name=" %input%') do (
  set "ln=%%A"
  setlocal enableDelayedExpansion
  call :parseLine
  endlocal
)
type %output%
exit /b

:parseLine
set "ln2=!ln:*<con:testSuite name=!"
if "!ln2!"=="!ln!" exit /b
for /f tokens^=2^ delims^=^" %%B in ("!ln2!") do (
  setlocal disableDelayedExpansion
  >>%output% echo(%%B
  endlocal
)
set "ln=!ln2!"
goto :parseLine

FINDSTR /N选项仅用于保证没有行以;开头,因此我们不必担心令人讨厌的默认FOR“EOL”选项。

打开和关闭延迟扩展的切换是为了保护输入文件中可能包含的任何!个字符。如果您知道!从未出现在输入中,那么您只需在顶部setlocal enableDelayedExpansion并删除所有其他setlocalendlocal命令。

最后一个FOR / F使用特殊的转义序列来指定双引号作为DELIM字符。

在评论中回答其他问题

您不能简单地将附加约束放在现有的FINDSTR命令中,因为它将返回具有匹配项的整行。记住你自己说过,“同一行中可能有不止一个匹配字符串的实例”。名字可能以正确的前缀开头,同一行的第二个名称可能不是。你只想保留适当开始的那个。

一种解决方案是简单地更改echo(%%B >>%output%行,如下所示:

echo(%%B|findstr "^lp_" >>%output%

FINDSTR正在使用正则表达式元字符^来指定该字符串必须以lp_开头。此时已经删除了引号,因此我们不必担心它们。

但是,您可能会遇到将来必须在搜索字符串中包含"的情况。另外,在初始FINDSTR中包含lp_屏幕可能会稍微快一些,因此不会不必要地调用:parseLine

FINDSTR要求使用反斜杠转义搜索字符串双引号。但Windows CMD处理器也有自己的转发规则。像>这样的特殊字符需要引用或转义。原始代码使用引号,但您希望在字符串中包含引号,并在命令中创建不平衡的引号。 Windows批量通常喜欢成对引用。至少有一个引号必须作为^"进行CMD转义。如果需要为CMD和FINDSTR转义引用,那么它看起来像\^"

但是,必须使用^转义字符串中不再从CMD透视功能引用的任何特殊字符。

这是一个逃避所有特殊字符的解决方案。它看起来很糟糕,而且很混乱。

for /f "delims=" %%A in ('findstr /n /c:^"^<con:testSuite^ name^=\^"lp_^" %input%') do (

这是另一个看起来好多了的解决方案,但是跟踪CMD的转义和FINDSTR转义的内容仍然令人困惑。

for /f "delims=" %%A in ('findstr /n /c:"<con:testSuite name=\"lp_^" %input%') do (

使事情变得简单的一种方法是将搜索转换为正则表达式。可以使用[\"\"]搜索单个双引号。它是一个匹配引号或引号的字符类表达式 - 我知道这很傻。但它保持引号配对,以便CMD感到高兴。现在您不必担心转义CMD的任何字符,您可以专注于正则表达式搜索字符串。

for /f "delims=" %%A in ('findstr /nr /c:"<con:testSuite name=[\"\"]lp_" %input%') do (