首页 > 解决方案 > 在定义的字符串之后查找特定字符串

问题描述

我正在尝试根据已安装库的 .xml 文件生成 .txt 报告。它应该报告图书馆 SNPID 编号| 图书馆名称 | 注册表项 | 图书馆路径。按升序排序,最后对齐列。

主要问题是当 .xml 文件只有一个 SNPID 和一个名称时脚本有效,但当 .xml 包含多个 SNPID 和名称时脚本无效

一些提示:

到目前为止,这是我的代码...

@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
    }
}

标签: xmlpowershellbatch-file

解决方案


我正在尝试根据已安装库的 .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 演示


推荐阅读