PowerShell 开关在退出时再次执行

PowerShell Switch Executing Again On Quit

我正在尝试编写一个带有多个开关的 PowerShell 菜单,但无法弄清楚为什么以前的 运行 命令会在退出时再次执行。任何帮助将不胜感激。到目前为止我的代码如下:

function Show-Menu {
    param (
        [string]$Title = 'Menu'
    )
    Clear-Host
    Write-Host "`n============= $Title =============`n"
    Write-Host "Press 'A' to run all commands"
    Write-Host "Press '1' to run foo"
    Write-Host "Press '2' to run bar"
    Write-Host "Press 'Q' to quit"
}

do {
    Show-Menu
    $Selection = Read-Host "`nPlease make a selection"

    switch ($Selection) {
        'A' {
            $Actions = @('foo', 'bar')
        }
        '1' {
            $Actions = "foo"
        }
        '2' {
            $Actions = "bar"
        }
    }
    
    switch ( $Actions ) {
        'foo' { 
            Write-Host "foo executed"
            Start-Sleep -Seconds 2
        }
        'bar' { 
            Write-Host "bar executed"
            Start-Sleep -Seconds 2
        }
    }
}
until ($Selection -eq 'q')

发生这种情况是因为您的 do...until 循环。您承诺在接受用户输入之前执行循环。由于是这种情况,$Actions 已经从循环的前一次迭代中设置,因此它 运行 是之前 运行.

这意味着如果您不为循环的每次迭代覆盖 $Actions,其他命令也会发生这种情况。

一个简单的解决方法是为 q 添加一个 case 以将 $Actions 设置为不在评估 $Actions 的 switch 语句中的内容。在这种情况下,应该使用空字符串。

如果您也需要它以类似的方式用于其他命令,而不是专门针对 q 的案例,您可以使用 default 案例来设置 $Actions变量。

简化。

与其将操作保存在变量中,然后采取另一步骤评估该变量...执行操作。

不要使用在单独位置检查退出条件的循环(即使用 until),而是使用无限循环和显式 break。这有助于将逻辑集中在一处。

function foo { Write-Host "foo"; Start-Sleep -Seconds 1 }
function bar { Write-Host "bar"; Start-Sleep -Seconds 1 }

:menuLoop while ($true) {
    Clear-Host
    Write-Host "`n============= Menu =============`n"
    Write-Host "Press 'A' to run all commands"
    Write-Host "Press '1' to run foo"
    Write-Host "Press '2' to run bar"
    Write-Host "Press 'Q' to quit"

    switch (Read-Host "`nPlease make a selection") {
        'A' { foo; bar }
        '1' { foo }
        '2' { bar }
        'Q' { break menuLoop }
    }
}

您的方法无法正常工作,因为在您的代码中,按下 Q 不会 立即 退出循环,并且 $Actions 仍然从最后一次迭代。

这是另一个教训:变量值不会在循环中自行重置。始终在循环开始时将变量设置为 $null 以获得干净的状态。

注意 :mainLoop 标签。没有它,break 将仅适用于 switch 语句本身。参见 MSDN


也就是说,PowerShell 内置了一个非常漂亮的菜单系统,您可以使用。

using namespace System.Management.Automation.Host

function foo { Write-Host "foo"; Start-Sleep -Seconds 1 }
function bar { Write-Host "bar"; Start-Sleep -Seconds 1 }

# set up available choices, and a help text for each of them
$choices = @(
    [ChoiceDescription]::new('run &all commands', 'Will run foo, and then bar')
    [ChoiceDescription]::new('&1 run foo', 'will run foo only')
    [ChoiceDescription]::new('&2 run bar', 'will run bar only')
    [ChoiceDescription]::new('&Quit', 'aborts the program')
)

# set up script blocks that correspond to each choice
$actions = @(
    { foo; bar }
    { foo }
    { bar }
    { break menuLoop }
)

:menuLoop while ($true) {
    $result = $host.UI.PromptForChoice(
        "Menu",                      # menu title
        "Please make a selection",   # menu prompt
        $choices,                    # list of choices
        0                            # default choice
    )

    & $actions[$result]              # execute chosen script block
}

运行 PowerShell ISE 和常规 PowerShell 中的这个,以查看它在每个环境中的行为。