xml - 在定义的字符串之后查找特定字符串
问题描述
我正在尝试根据已安装库的 .xml 文件生成 .txt 报告。它应该报告图书馆 SNPID 编号| 图书馆名称 | 注册表项 | 图书馆路径。按升序排序,最后对齐列。
主要问题是当 .xml 文件只有一个 SNPID 和一个名称时脚本有效,但当 .xml 包含多个 SNPID 和名称时脚本无效
一些提示:
- .xml 文件中搜索的字符串/行总是有附加空格
- SNPID 也可以有字母。
- 在里面
<producthint>
</producthint>
我们可以有多个<product>
- 在里面
<product>
</product>
我们可以有多个<flavour>
</flavour>
- 顺序始终相同,名称为第一:
<Name></Name>
<SNPID></SNPID>
- 但名称/SNPID 可以在内部
<product>
</product>
或内部<flavour>
</flavour>
- 当内部有
<flavour>
值时<product>
,产品的 RegKey 对于所有口味都是相同的。
到目前为止,这是我的代码...
@echo off
pushd "%~dp0"
setlocal
set "Backup_Folder=%~dp0NI_Backup"
set "SNPID_List=%Backup_Folder%\SNPID_Report.txt"
set "SNPID_TMP_List_To_Order=%Backup_Folder%\SNPID_TMP_List_To_Order.txt"
set "SNPID_TMP_List_To_Realign=%Backup_Folder%\SNPID_TMP_List_To_Realign.txt"
set "XML_Dir=C:\Program Files\Common Files\Native Instruments\Service Center"
set "Registry_Key=HKLM\SOFTWARE\Native Instruments"
if not exist "%Backup_Folder%" ( mkdir "%Backup_Folder%" >nul 2>&1 )
setlocal EnableDelayedExpansion
REM Loop through .xml
(
for /f "delims=" %%a in ('dir /s/b/a-d "%XML_Dir%\*.xml"^| find /v "NativeAccess" ^| find /v "ProductHints" ^| find /v "Maschine 2"') do (
for /f "usebackqtokens=1-3delims=<>" %%E in ("%%a") do (
if "%%F"=="SNPID" (
for /f "usebackqtokens=1-3delims=<>" %%I in ("%%a") do (
if "%%J"=="Name" (
for /f "usebackqtokens=1-3delims=<>" %%M in ("%%a") do (
if "%%N"=="RegKey" (
for /f "tokens=2*" %%Q in ('reg query "%Registry_Key%\%%O" /v "ContentDir" 2^>nul ') do (
set "ContentDir=%%R"
if "!ContentDir:~1,2!"==":\" ( echo %%G ^| %%K ^| %Registry_Key%\%%O ^| %%R )
))))))))
)>"%SNPID_TMP_List_To_Order%"
REM Rename Paths ending with backslash
call "%~dp0Jrepl.bat" "(.*)\\$" "$1" /xseq /m /f "%SNPID_TMP_List_To_Order%" /o -
REM Remove duplicates
call "%~dp0Jrepl.bat" "\c([\c\r\n]+)\r?\n(?=[\s\S]*\c\1$)" "" /xseq /m /f "%SNPID_TMP_List_To_Order%" /o -
REM echo column title
echo SNPID^| Library Name^| Registry Key^| Library Location>"%SNPID_TMP_List_To_Realign%"
REM Sort by SNPID number
sort <"%SNPID_TMP_List_To_Order%" >>"%SNPID_TMP_List_To_Realign%"
REM Get Columns length
set "SNPID_MaxLength=0"
set "LibName_MaxLength=0"
set "RegKey_MaxLength=0"
for /f "usebackqtokens=1-3 delims=|" %%a in ("%SNPID_TMP_List_To_Realign%") do (
set "String=%%a" & call :strlen
for /f "tokens=* delims=0" %%B in ("!result!") do (
if %%B gtr !SNPID_MaxLength! set "SNPID_MaxLength=%%B"
)
set "String=%%b" & call :strlen
for /f "tokens=* delims=0" %%B in ("!result!") do (
if %%B gtr !LibName_MaxLength! set "LibName_MaxLength=%%B"
)
set "String=%%c" & call :strlen
for /f "tokens=* delims=0" %%B in ("!result!") do (
if %%B gtr !RegKey_MaxLength! set "RegKey_MaxLength=%%B"
)
)
REM Set Columns Spacing
set "Space_Count=%SNPID_MaxLength%"
set "SNPID_Space= "
set "LibName_Space= "
set "RegKey_Space= "
:SNPID_Spacing
if "%Space_Count%"=="0" ( set "Space_Count=%LibName_MaxLength%" & goto :LibName_Spacing )
set "SNPID_Space=%SNPID_Space% "
set /a "Space_Count-=1"
goto :SNPID_Spacing
:LibName_Spacing
if "%Space_Count%"=="0" ( set "Space_Count=%RegKey_MaxLength%" & goto :RegKey_Spacing )
set "LibName_Space=%LibName_Space% "
set /a "Space_Count-=1"
goto :LibName_Spacing
:RegKey_Spacing
if "%Space_Count%"=="0" ( goto :Realign_Columns )
set "RegKey_Space=%RegKey_Space% "
set /a "Space_Count-=1"
goto :RegKey_Spacing
REM Columns Alignment
:Realign_Columns
(
for /f "usebackqtokens=1-4 delims=|" %%a in ("%SNPID_TMP_List_To_Realign%") do (
set "SNPID_Aligned=%%a%SNPID_Space%" & set "SNPID_Aligned=!SNPID_Aligned:~0,%SNPID_MaxLength%!"
set "LibName_Aligned=%%b%LibName_Space%" & set "LibName_Aligned=!LibName_Aligned:~0,%LibName_MaxLength%!"
set "RegKey_Aligned=%%c%RegKey_Space%" & set "RegKey_Aligned=!RegKey_Aligned:~0,%RegKey_MaxLength%!"
echo !SNPID_Aligned!^|!LibName_Aligned!^|!RegKey_Aligned!^|%%d
)
)>"%SNPID_List%"
endlocal
REM del "%SNPID_TMP_List%" "%SNPID_TMP_List_To_Order%" "%SNPID_TMP_List_To_Realign%"
pause & exit /b
:strlen
(
(set^ tmp=!String!)
set "len=1"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!tmp:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "tmp=!tmp:~%%P!"
))
)
(
set "result=!len!"
exit /b
)
(我在这里简化了 .xml 文件)
<?xml version="1.0" encoding="UTF-8"?>
<ProductHints>
<Product version="3">
<Name>Battery 4</Name>
<Company>Company Name</Company>
<some value>xxx</some value>
<RegKey>Battery</RegKey>
<some value>yyy</some value>
<BingName>Battery 4</BingName>
<SNPID>249</SNPID>
</Product>
</ProductHints>
会给:
249 | Battery 4 | Battery | PATH
尽管
<?xml version="1.0" encoding="UTF-8"?>
<ProductHints>
<Product version="3">
<Name>Maschine 2</Name>
<Company>Company Name</Company>
<SNPID>334165166167</SNPID>
<RegKey>Maschine 2</RegKey>
<Flavour>
<Name>Maschine 2</Name>
<SNPID>334</SNPID>
<Value>0</Value>
</Flavour>
<Flavour>
<Name>Maschine 2 Essentials</Name>
<Value>-1</Value>
<SNPID>165</SNPID>
</Flavour>
<Flavour>
<Name>Another Flavour</Name>
<SNPID>166</SNPID>
<some value>yyy</some value>
</Flavour>
<Flavour>
<Name>Another Flavour2</Name>
<some value>yyy</some value>
<SNPID>167</SNPID>
</Flavour>
</Product>
<Product version="3">
<Name>Battery 4</Name>
<Company>Company Name</Company>
<some value>xxx</some value>
<RegKey>Battery</RegKey>
<some value>yyy</some value>
<BingName>Battery 4</BingName>
<SNPID>249</SNPID>
</Product>
</ProductHints>
会给:
165 | Maschine 2
166 | Maschine 2
167 | Maschine 2
334 | Maschine 2
249 | Maschine 2
165 | Maschine 2 Essentials
166 | Maschine 2 Essentials
167 | Maschine 2 Essentials
334 | Maschine 2 Essentials
249 | Maschine 2 Essentials
165 | Another Flavour
166 | Another Flavour
167 | Another Flavour
334 | Another Flavour
249 | Another Flavour
etc...
但应该只是:
165 | Maschine 2
334 | Maschine 2 Essentials
249 | Battery 4
166 | Another Flavour
167 | Another Flavour2
optionally, also
334165166167| Maschine 2
or without... (would like to see both outputs)
意义:
始终将 SNPID 与其内部的 NAME 相关联<Product>
</Product>
,除非内部有一个<flavour>
值,否则<Product></Product>
我将 SNPID 与内部的 NAME 相关联<flavour>
</flavour>
。
在有味道的情况下省略第一个 NAME,或者没有......我需要查看两个不同的解析输出来选择。
NAME 始终高于其 SNPID 编号,但其间可能有一些值(以及不同数量的行)
当存在风味值时,风味 RegKey 是 Product Regkey
更新:使用 PS 解析尝试更新问题,它运行良好,但我无法解决“风味问题”,对我来说太难了......也无法在 UTF8NOBOM 中输出
"DummyLine" | Out-File "$PSScriptRoot\Parsed_List.txt" -Encoding UTF8
$items = Get-ChildItem "C:\Program Files\Common Files\Native Instruments\Service Center\*.xml"
foreach ($item in $items) {
[xml]$XML_File = Get-Content $item
$XML_File.ProductHints.Product | % {
$Name = $_.Name
$RegKey = $_.RegKey
If (-Not $_.SNPID) {$SNPID = "ThirdParty"} Else {$SNPID = $_.SNPID}
If (-Not $_.Company) {$Company = "Not specified"} Else {$Company = $_.Company}
If ($SNPID -eq "334165") {$Name = "Maschine 2 Essential";$SNPID = "165"}
"$SNPID`|$Name`|$Company`|$RegKey`|Location" | Out-File "$PSScriptRoot\Parsed_List.txt" -append -Encoding UTF8
}
}
解决方案
我正在尝试根据已安装库的 .xml 文件生成 .txt 报告。它应该报告图书馆 SNPID 编号| 图书馆名称 | 注册表项 | 图书馆路径。按升序排列...
我对 PowerShell 及其功能不是很熟悉,但由于 Batch 的局限性,使用本机 Batch 函数解析 XML 是一个非常糟糕的主意。我想您已经亲眼目睹了批处理脚本可以变得多么复杂。使用 regex 解析 XML
甚至更糟糕(对 Dave Benham 的'jrepl.bat'的惊人工作没有冒犯)。
请使用像Xidel这样的真正的 XML 解析器:
xidel -s input.xml --xquery "for $x in (//Product,//Flavour) order by $x/SNPID return $x/join((SNPID,Name,(RegKey,../RegKey)),' | ')"
165 | Maschine 2 Essentials | Maschine 2
166 | Another Flavour | Maschine 2
167 | Another Flavour2 | Maschine 2
249 | Battery 4 | Battery
334 | Maschine 2 | Maschine 2
334165166167 | Maschine 2 | Maschine 2
如果我正确理解您的问题,这就是您要查找的内容:
- 获取“产品”-以及“风味”-元素节点并按“SNPID”排序。
- 抓住前面提到的兄弟姐妹并用“|”分隔加入他们。
目前我无法帮助您处理第四个元素“ | Library Path ”,只是因为我没有那些注册表项。
...最后对齐列。
这有点困难,但这是可能的。它基本上归结为:
- 将上面的输出放在一系列数组中并分配给一个变量。
- 计算每个“列”的最大宽度。
- 为每个值添加空格以达到此宽度。
xidel -s input.xml --xquery ^"^
let $a:=for $x in (//Product,//Flavour)^
order by $x/SNPID^
return $x/[^
SNPID,^
Name,^
(RegKey,../RegKey)^
],^
$b:=(1 to count($a)) ! max(^
$a(.) ! string-length()^
)^
return^
$a ! join(^
for $x at $i in .() return^
substring(^
$x^|^|string-join((1 to $b[$i]) ! ' '),^
1,$b[$i]^
),^
' ^| '^
)^
"
165 | Maschine 2 Essentials | Maschine 2
166 | Another Flavour | Maschine 2
167 | Another Flavour2 | Maschine 2
249 | Battery 4 | Battery
334 | Maschine 2 | Maschine 2
334165166167 | Maschine 2 | Maschine 2
这是一个带有必要转义字符的“美化”查询(在每行的末尾和每个|
),您可以直接在命令提示符处复制粘贴。
另请参阅此在线 xidelcgi 演示。
推荐阅读
- arrays - 比较 A 和 B 列表并创建一个 C 列表,其中 B 值不在 Powershell 的 A 列表中
- python - 关于如何在 Python 中创建 CrossTab 的困惑
- c# - StarSchema - 实体框架核心 - 迁移
- r - 计算人口年龄四分位数范围并将结果存储为 R 中的数据框的最佳方法
- r - 将几个表连接在一起
- javascript - 尝试使用 Typescript 和 Parcel-Bundler 渲染 Google 地图时出错
- javascript - useEffect 在井字游戏中无法正常工作
- javascript - 调用 setState 时组件不会重新渲染
- c# - 使用 Blazor 和 XML 构建动态用户界面
- nuxt.js - 如何创建一个 nuxt 存储子模块,以便我可以使用与第二个实例相同的存储模块?