Powershell 最后块用 Ctrl-C 跳过

Powershell Finally block skipped with Ctrl-C

我正在使用 Try/Finally 在 Powershell 中编写一个监控脚本,以便在脚本结束时记录一条消息。该脚本旨在无限期地 运行,所以我想要一种方法来跟踪意外退出。

所有其他 Whosebug post 和 Help page 我已经检查过以下状态:

A Finally block runs even if you use CTRL+C to stop the script. A Finally block also runs if an Exit keyword stops the script from within a Catch block.

在实践中,我没有发现这是真的。我正在使用以下人为设计的示例来对此进行测试:

Try {
    While($True) {
        echo "looping"
        Start-Sleep 3
    }
} Finally {
    echo "goodbye!"
    pause
}

这里的Finally块在每次Ctrl+C之后都会跳过(没有回显,没有停顿),当 运行ning 作为保存的脚本或通过内置的 Powershell ISE 执行时。我得到的唯一输出是:

looping
looping
...(repeat until Ctrl-C)

我显然漏掉了什么,但我不知道它是什么,尤其是在这么小的代码片段中。

功能代码

这会给你我相信你想要的行为:

Try {
    While($True) {
        echo "looping"
        Start-Sleep 3
    }
} Finally {
    Write-Host "goodbye!"
    pause
}

参考资料

Write-Output/echo - Synopsis

Sends the specified objects to the next command in the pipeline. If the command is the last command in the pipeline, the objects are displayed in the console.

Write-Host - Synopsis

Writes customized output to a host.

Try-Catch-Finally - Syntax note

Note that pressing CTRL+C stops the pipeline. Objects that are sent to the pipeline will not be displayed as output. Therefore, if you include a statement to be displayed, such as "Finally block has run", it will not be displayed after you press CTRL+C, even if the Finally block ran.

说明

根据 TheIncorrigible1 的评论和 Vesper 的回答,关键是管道停止。但这并不是因为 Write-Output 中的错误。而且我认为它本身并不是一个令人满意的解释。

  • "If the command is the last command in the pipeline, the objects are displayed in the console." - 在 finally 块中出现此语句是错误的。但是,显式传递给 Out-Host 将产生所需的输出。
  • 关于 Try-Catch-Finally 注释
    • 引用的部分令人困惑,因为它适用于 未处理的对象 发送到管道。
    • 发送到管道并在 Finally 块内处理的对象没问题。
    • 它谈论 "even if the Finally block has ran"pause 不会 运行 如果前面有 Write-Output.

更多代码

Finally 块中的几件事 运行 用于调查行为,并对发生的情况发表评论。

} Finally {
    Write-Output "goodbye!" | Out-Default # works fine
    pause
}

} Finally {
    Write-Output "goodbye!" | Out-Host    # works fine
    pause
}

} Finally {
    pause                     # works fine
    Write-output "goodbye!"   # not executed
}

} Finally {
    try{
        Write-Output "goodbye!" -ErrorAction Stop
    }catch{
        Write-Host "error caught"   # this is not executed.
    }                               # $error[0] after script execution is empty
    pause
}

} Finally {
    try{
        ThisCommandDoesNotExist
    }catch{
        Write-Host "error caught"   # this is executed
    }                               # $error[0] contains CommandNotFoundException      
    pause
}

正确的答案是 Ctrl+C 中断管道,如 link 中所述,并且 echo 使用管道来处理它的输出。因此,一旦您 Ctrl+C,写入管道会导致脚本块出错,并且不会处理任何进一步的命令。因此,不要使用直接将输出发送到 stdout 的命令,并且有很多间接使用管道的命令。另一方面,Write-Host 不使用管道,因此不会引发错误。