Powershell CommandNotFoundException 正在终止?

Yoa*_*ein 2 powershell

执行这样的脚本时:

nonexistingcommand
echo "Hello"
Run Code Online (Sandbox Code Playgroud)

我得到:

nonexistingcommand : The term 'nonexistingcommand' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the 
name, or if a path was included, verify that the path is correct and try again.
At D:\Playground\powershell\Test.ps1:2 char:1
+ nonexistingcommand
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (nonexistingcommand:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 
Hello

Run Code Online (Sandbox Code Playgroud)

所以看起来这CommandNotFoundException是一个非终止错误。那么为什么如果我

try {
 nonexistingcommand
}
catch [System.Management.Automation.CommandNotFoundException] {
  throw
}
echo "Hello"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它将退出并且不打印“Hello”。

这是为什么?

mkl*_*nt0 5

不幸的是,PowerShell 有两种类型的终止错误:

  • 语句终止错误,默认情况下仅终止封闭的语句

    • 默认情况下,执行恢复,即使用下一条语句(echo "Hello"在您的情况下)

    • CommandNotFoundException是此类错误的一个实例。

    • 相比之下,如果进一步的管道输入可用,非终止Get-ChildItem NoSuchDir, \错误甚至会继续处理原始语句(例如,针对不存在的目录发出非终止错误,NoSuchDir然后继续成功报告现有目录。\

  • 脚本终止(线程终止)错误,它终止封闭的脚本和整个调用堆栈

    • 此类错误是由PowerShell 代码调用的throw语句触发的,而不是由二进制(编译的)cmdlet 触发的。

try { ... } catch { ... } finally { ... }语句区分这两种子类型:它同时捕获它们。

  • 鉴于您的catch块包含一个无参数throw语句,该语句隐式中继触发错误的错误,您实际上将语句终止错误转换为脚本终止错误,因此执行会在那里结束。

同样,将$ErrorActionPreference首选项变量设置为会'Stop'导致所有类型的错误(由PowerShell命令发出)[1]),包括终止错误,从而中止整体执行;换句话说:终止错误和语句终止错误都会提升为脚本终止错误。


有关PowerShell 极其复杂的错误处理的全面概述,请参阅GitHub 文档问题 #1583


[1] 至少在控制台(终端)中外部程序的本地前台调用中,stderr输出默认情况下通过 PowerShell 的错误流路由,因此不受$ErrorActionPreference.
但是,在 PowerShell v7.1 及更低版本(包括在 Windows PowerShell 中)中,使用2>重定向确实通过 PowerShell 的错误流路由 stderr,这可能会产生意外的副作用。该问题将在 v7.2 中修复。
请注意,该行为是 PowerShell主机特定的并且适用于主机ConsoleHost(用于$host获取主机信息)。其他主机仍然总是通过错误流路由 stderr,这显着影响远程处理后台作业