PowerShell开关在退出时再次执行



我正试图用多个开关编写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')

简化。

与其将操作保存在变量中,然后采取另一个步骤来评估该变量。。。做动作。

使用循环和显式break,而不是在单独的位置检查退出条件(即使用until(。这有助于将逻辑保持在一个位置。

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中运行此程序,查看它在每个环境中的行为。

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

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

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

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

最新更新