我在这里画一个空白,我确信答案就在我面前。我正在尝试构建一个动态菜单,以便在WinPE映像中启动小型应用程序。我正在使用XML文件来获取数据(这似乎是最合适的)
现在我的xml文件看起来像这样
<Programs>
<!-- Programs -->
<Program Name="7-Zip" Path="\7-Zip\7zFM.exe" Type="Addin"/>
<Program Name="HwMonitor" Path="\HwMonitor\HwMonitor.exe" Type="Addin"/>
<!-- HDTools -->
<Program Name="Defraggler" Path="\Defraggler64\Defraggler64.exe" Type="Hdtools"/>
</Programs>
我遇到了问题,我写的函数是采用$Type
参数,所以我可以根据类型进行过滤。过滤器本身工作正常,它是下面用于构建菜单的部分....只有当多个项目符合该条件时才有效。
Function Select-Program($Type)
{
clear; MenuHeader
Write-Host " ----------------------------------------------------------------------- "
$List = $null # set the list to null, for some reason there was duplication when re-entering the menu
try
{
$AddinPath = switch([System.IntPtr]::Size)
{
8 {Join-Path (Get-PEDrive).DriveLetter "Programs64"}
4 {Join-Path (Get-PEDrive).DriveLetter "Programs"}
}
$ListFile = Join-Path $AddinPath "Programs.xml"
[xml]$Programs = Get-Content $ListFile
Write-Host " "
$List = $Programs.DocumentElement.Program | Where-Object {$_.Type -eq $Type}
}
catch [exception]
{
$_.Exception
}
# if there's no items in the list, then write a message
if ($List.Count -eq 0)
{
Write-Host " No Programs Found" -ForegroundColor Red
Write-Host " "
}
$menu = @{}
# build menu item list dynamically from the imported data
for ($i=1;$i -le $List.count; $i++)
{
Write-Host " $i. $($List[$i-1].Name)" -foregroundcolor White
$menu.Add($i,($List[$i-1].Name))
}
# add one last item to return to main menu
Write-Host " $i. Return to main menu" -foregroundcolor White
$menu.Add($i,"return")
#prompt for selection
Write-Host " "
[int]$ans = Read-Host ' Enter selection'
$selection = $menu.Item($ans)
if ($selection -eq "return")
{
MainMenu
}
# launch the selected application
$Launch = Join-Path $AddinPath ($List | Where-Object {$_.Name -eq $selection}).Path
Start-Process $Launch
Select-Program -Type $Type # reload the program menu
}
如果我使用上面的XML运行“Select-Program -Type Addin”,我会得到一个包含2个项目的菜单,以及返回主菜单。但是,如果我运行“Select-Program -Type Hdtools”,我什么也得不到,因为XML文件中只有一个具有此类型的项目。
今天早上我尝试使用CSV文件,我得到同样的东西,所以我不认为它与数据的来源或我从哪种格式中提取它有任何关系。
我在这里缺少什么?我不敢操作员弄乱了我,所以我尝试了不到或者相等,但是我也遭到了轰炸,让我现在挠挠脑袋
答案 0 :(得分:1)
我打算提出一种不同的方法,我认为这会简化整个过程。
首先,生成表示您在XML中存储的内容的对象,然后将它们导出为CliXML或JSON:
$objects = @()
$objects += [PSCustomObject]@{
Name = "7-Zip"
Path = "\7-Zip\7zFM.exe"
Type = "Addin"
}
$objects += [PSCustomObject]@{
Name = "Defraggler"
Path ="\Defraggler64\Defraggler64.exe"
Type = "Hdtools"
}
# etc.
$objects | Export-CliXml -Path C:\my.xml
$objects | ConvertTo-Json | Set-Content -Path C:\my.json
现在,当你想把它们带回来时:
# from xml
$List = Import-CliXml -Path C:\my.xml
# or from JSON
$List = Get-Content -Path C:\my.json -Raw | ConvertFrom-Json
$List = $List | Where-Object { $_.Type -eq $Type }
$menu = @{}
for ($i=1;$i -le $List.count; $i++)
{
Write-Host " $i. $($List[$i-1].Name)" -foregroundcolor White
$menu.Add($i,($List[$i-1].Name))
}
我认为这简化了以一致方式导出和导入对象的过程。该数组应该作为导出过程的一部分进行序列化,并作为导入的一部分进行反序列化,因此您不必担心返回单个对象的方法或cmdlet。
答案 1 :(得分:0)
我解决了。问题是,当只有一个项目时,PowerShell会以不同的方式处理数组。我遇到了从CSV导入的相同行为。这似乎特别是通过Where-Object cmdlet管道数据
当只有一个项目从通过Where-Object传送的XML $ List变量中提取时,$ List [0] .Name返回一个空字符串,其中$ List.Name返回所需的值。
解决方案是改变这个
$menu = @{}
for ($i=1;$i -le $List.Name.count; $i++)
{
$DisplayName = switch([string]::IsNullOrEmpty($List[$i-1].Name))
{
$true {$List.Name}
$false {$List[$i-1].Name}
}
Write-Host ([string]::Format(" {0}. {1}", $i, $DisplayName)) -foregroundcolor White
$menu.Add($i,($DisplayName))
}
到此
g_main_loop_run(loop);
主要关注项目如下
- 将$ List.count更改为$ List.Name.count,因为只有当项目在XML数据中时,$ List.Count返回空值或空值
-change -lt(小于)到-le(小于或等于),因为只有一个项目且$ i值从1开始小于永远不符合标准
-added一个switch变量来检查$ List [$ i-1] .Name是否返回null,因为它只返回一个项目。在这种情况下,请使用$ List.Name
- 使用string.Format方法输出行,不是必需的,但我发现在某些情况下它是一种更可预测的连接字符串的方法