`echo $?` 是如何工作的?

4
我正在编写一些PowerShell脚本,用于构建自动化。我在这里找到了一个答案,链接,其中echo $?会根据上一个语句返回true或false。我刚刚发现echo是Write-Output的别名。Write-Host $?也可以工作。但我还不清楚这个$?是如何工作的。有人可以友善地说几句吗?在网上搜索echo $?并没有给我带来太多收获。
2个回答

5

为了补充 Martin Brandl的有用回答,以下提供更详细的信息:

简而言之

  • 自动变量$?(参见Get-Help about_Automatic Variables)包含一个布尔值,反映最近语句中的任何非终止错误是否发生

    • 由于$?在每个语句后设置,因此您必须在感兴趣的语句之后立即检查它,或将其保存以供以后检查。
    • 有关可能具有违反直觉行为的内容,请参见下文。
  • 自动变量$LASTEXITCODE通过记录最近执行的外部命令行实用程序的特定退出代码(控制台应用程序,例如findstr)来补充这一点。

    • $LASTEXITCODE作为补充说明$?,因为$?仅反映外部实用程序的抽象成功或失败 - 将退出代码0映射到$True,将任何非零退出代码映射到$False。而$LASTEXITCODE包含实际的退出代码。
    • 由于$LASTEXITCODE仅适用于外部命令行实用程序,因此其值通常比$?更长时间有效。

关于如何设置$?以及它的值表示什么,有许多微妙之处

在 PowerShell 版本 低于 v7.2 中,$? 存在外部命令行实用程序 误报的风险,即使 $LASTEXITCODE0,也会报告 $false,具体情况可以参见此答案$? 只反映了非终止性错误的发生,因为(较为罕见的)终止性错误默认会终止当前命令行/脚本的执行。要处理这些错误,您需要使用 try / catch(首选)或 trap(请参见Get-Help about_Try_Catch_FinallyGet-Help about_Trap)。
相反,您可以选择将非终止性错误视为终止性错误,使用首选变量 $ErrorActionPreference 或常用 cmdlet 参数 -ErrorAction(别名 -EA),详情请参见Get-Help about_Preference_VariablesGet-Help about_CommonParameters
除非明确使用通用的 -ErrorAction Ignore cmdlet 参数忽略,否则所有非终止性错误(和已捕获的终止性错误)都将被收集在自动的 $Error 集合中,以时间顺序倒序排列;也就是说,元素 $Error[0] 包含了最近的错误。
对于传递了多个输入对象的命令,$? 只告诉您至少有一个输入对象处理失败。换句话说:任何子集的输入对象都可能发生错误,包括全部输入对象。 要确定确切的错误计数和引起错误的输入对象,您必须检查 $Error 集合
对于您传递要执行的目标命令的非远程间接执行 cmdlet,例如 Invoke-ExpressionStart-ProcessStart-Job 以及不带 -ComputerName 参数的 Invoke-Command(不涉及远程操作-请参见下文),$? 只反映了目标命令是否能够原则上被调用,不管该命令是否报告错误。
一个简单的例子: Invoke-Expression '1/0'设置 $?$True(!),因为 Invoke-Expression 能够解析和执行表达式,即使表达式本身失败。
同样,检查 $Error 集合会告诉您目标命令是否报告了错误以及错误内容。
对于远程(无论如何都是间接执行的)cmdlet,特别是带有 -ComputerName 参数的 Invoke-Command(通常是这样的),
  • Examples of commands that DO reflect errors in $?:

      # Invoking a non-existing cmdlet or utility directly.
      NoSuchCmd
    
      # Ditto, via call operator &.
      # Note, however, that using a *script block* with & behaves differently - see below.
      & 'NoSuchCmd'
    
      # Invoking a cmdlet with invalid parameter syntax.
      Get-ChildItem -NoSuchParameter
    
      # Invoking a cmdlet with parameter values that cause a (non-terminating) runtime error.
      Get-ChildItem NoSuchFile
    
      # Invoking an external utility that reports a nonzero exit code. 
      findstr -nosuchoptions
      # The specific exit code is recorded in $LASTEXITCODE, 
      # until the next external utility is called.
    
      # Runtime exceptions
      1 / 0
    
      # A cmdlet that uses remoting:
      # (Must be run from an elevated session, and the local machine must
      # be configured for remoting first - run `winrm quickconfig`).
      # Note that remoting would NOT be involved WITHOUT the -ComputerName parameter, 
      # in which case `$?` would only reflect whether the script block could be
      # _invoked_, irrespective of whether its command(s) then fail or not.
      Invoke-Command -ComputerName . { 1 / 0 }
    
      # A .NET method that throws an exception.
      # Note: Outside of a `try/catch` handler, this is a non-terminating error.
      # Inside a `try/catch` handler, .NET exceptions are treated as terminating
      # and trigger the `catch` block.
      [System.IO.Path]::IsPathRooted('>')
    
  • Examples of commands that DO NOT reflect errors in $?:

      <#
        Non-remoting indirect execution cmdlets:
    
        $? reflects only whether the specified command could be 
        *invoked*, irrespective of whether the command itself then failed or not.
    
        In other words: $? is only $False if the specified command could not even be
        executed, such as due to invalid parameter syntax, an ill-formed target
        command, or a missing target executable. 
    
      #>
    
      # Invoking a command stored in a script block.
      & { 1 / 0 }
    
      # Invoking an expression stored in a string.
      Invoke-Expression '1 / 0'
    
      # Starting a background job.
      Start-Job { 1/ 0 }
    
      # The *non-remoting* form of Invoke-Command (WITHOUT -ComputerName).
      Invoke-Command { 1 / 0 }
    

4
你可以在这里找到完整的标点符号图表。答案(来自图表):

上一个操作的执行状态($true 或 $false);与报告上一个 Windows 程序的退出代码的 $LastExitCode 相对比。


1
感谢马丁提供的图表,非常简洁明了。在这个图表中,在底部我找到了这个链接,我猜这是官方文档。 - VivekDev
1
是的,那是官方文档。不用谢。 - Martin Brandl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接