I'm trying generating a .txt report based on .xml files for installed libraries. It should report Library SNPID Number| Library Name | Registry Key | Library Path. Sort by ascending number and finally align columns.
Main problem is script works when .xml file has only one SNPID and one Name but not when .xml includes multiple SNPID and Names
Some hints:
- Searched strings/lines in .xml files ALWAYS have appending spaces
- SNPID can also have letters.
- Inside
<producthint>
</producthint>
we can have multiple<product>
- Inside
<product>
</product>
we can have multiple<flavour>
</flavour>
- The order is always the same, Name is 1st:
<Name></Name>
<SNPID></SNPID>
- But Name/SNPID can be either inside
<product>
</product>
or inside<flavour>
</flavour>
- When there is the
<flavour>
value inside<product>
, Product's RegKey is same for all flavours.
Here's my code so far...
@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
)
(I simplified the .xml files here)
<?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>
will give:
249 | Battery 4 | Battery | PATH
while
<?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>
will give:
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...
but should be only:
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)
Meaning:
Always associate SNPID with its NAME inside <Product>
</Product>
, unless there is a <flavour>
value inside <Product></Product>
then I associate SNPID with the NAME which is inside <flavour>
</flavour>
.
Omiting first NAME in the case there is flavour, or not...I would need to look the two different parsed output to choose.
NAME is always above its SNPID number but there can be some values in between (and a different number of lines)
When there the flavour value is present, flavour RegKey is Product Regkey
Update: Updated question with PS parsing try, it's working well but I can't solve the "flavour thing", too hard for me...also can't output in 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
}
}