【问题标题】:PowerShell dynamic menu from xml来自 xml 的 PowerShell 动态菜单
【发布时间】:2015-12-19 11:28:00
【问题描述】:

我在这里画了一个空白,我确信答案就在我面前。我正在尝试构建一个动态菜单来启动 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 文件,但得到了同样的结果,所以我认为这与数据来自哪里或我从什么格式中提取数据无关。

我在这里缺少什么?我觉得小于运算符把我搞砸了,所以我尝试了小于或等于,但这也轰炸了我,让我现在摸不着头脑

【问题讨论】:

  • $list 转换为数组,看看它是否有效.....`$List = @($Programs.DocumentElement.Program | Where-Object {$_.Type -eq $Type })
  • 我发布了一个与您的中心问题背道而驰的答案,因为我认为从长远来看这可能是一种更好的方法。但至于您为什么会遇到问题,这将有助于查看您收到的错误消息。
  • 这就是问题所在,我没有收到错误消息。如果 XML 文件中只有一项,我什么也得不到。如果我添加第二个项目,它工作得很好。

标签: xml powershell


【解决方案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 返回单个对象。

【讨论】:

    【解决方案2】:

    我解决了。问题是当只有一个项目时,PowerShell 以不同的方式处理数组。我遇到了从 CSV 导入的相同行为。这似乎是专门通过 Where-Object cmdlet 管道传输数据的结果

    当仅从通过 Where-Object 管道传输的 XML $List 变量中提取一项时,$List[0].Name 返回一个空白字符串,其中 $List.Name 返回所需的值。

    解决办法是改变这个

    $menu = @{}
    for ($i=1;$i -lt $List.count; $i++)
    {
        Write-Host "  $i. $($List[$i-1].Name)" -foregroundcolor White
        $menu.Add($i,($List[$i-1].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))
    }
    

    主要关注的项目如下

    -将 $List.count 更改为 $List.Name.count,因为当 XML 数据中只有 item 时,$List.Count 返回 null 或空值

    -将 -lt(小于)更改为 -le(小于或等于),因为只有一项且 $i 值从小于 1 开始永远不会满足条件

    - 添加了一个开关变量来检查 $List[$i-1].Name 是否返回空值,就像它只对单个项目所做的那样。在这种情况下,使用 $List.Name

    -对输出行使用 string.Format 方法,不是必需的,但我发现在某些情况下它是一种更可预测的连接字符串的方法

    【讨论】:

      猜你喜欢
      • 2016-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多