echo $如何?工作?

How does echo $? work?

我正在编写一些 PowerShell 脚本来进行一些构建自动化。我发现 here echo $? returns true 或 false 取决于前面的陈述。我刚刚发现 echo 是 Write-Output 的别名。 Write-Host $? 也可以。但是我仍然不清楚 $? 是如何工作的。有人可以就此说几句话吗?搜索回声 $?网上没给我太多。

您找到了完整的标点符号表 here。答案(取自图表):

Execution status of the last operation ($true or $false); contrast with $LastExitCode that reports the exit code of the last Windows-based program executed.

用更详细的信息补充

tl;dr

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

    • 由于 $? 设置在 每个 语句之后,您必须在感兴趣的语句之后立即检查它,或者保存它以供以后检查。
    • 请参阅下文了解可能违反直觉的行为。
  • 自动变量$LASTEXITCODE通过记录[=97来补充 =]最近执行的外部命令行实用程序(例如findstr)的特定退出代码。

    • $LASTEXITCODE 补充 $?,因为 $? 仅反映 抽象 外部实用程序的成功或失败 - 退出代码 0 映射到 $True,任何非零退出代码映射到 $False - 而 $LASTEXITCODE 包含实际退出代码。
    • 由于 $LASTEXITCODE 仅为外部命令行实用程序设置,因此其值通常比 $? 更有效,后者在 every 语句之后设置.

关于 $? 的设置方式,以及它的值准确表示的内容,有很多 细节

  • $? 仅反映 非终止 错误 的发生,因为(更罕见)terminating 错误默认终止当前命令行/脚本的执行,要处理它们,您需要使用 try / catch(首选)或 trap(参见 Get-Help about_Try_Catch_Finally and Get-Help about_Trap).

  • 除非明确忽略(使用常见的 -ErrorAction Ignore cmdlet 参数),所有非终止错误(并捕获终止errors) 收集在自动 $Error 集合 中,按时间倒序排列;也就是说,元素 $Error[0] 包含最新的错误。

  • 对于多个输入对象被传递到的命令,包含$False$?只告诉你至少一个 输入对象的处理失败。换句话说:输入对象的任何子集都可能发生错误,包括所有

    • 要确定确切的错误计数和有问题的输入对象,您必须检查 $Error 集合。
  • 使用非远程间接执行 cmdlets,您向其传递目标命令以执行 - 例如Invoke-ExpressionStart-ProcessStart-JobInvoke-Command 没有 -ComputerName 参数(不涉及远程处理 - 请参阅below) - $? 原则上只反映目标命令是否可以被调用,不管该命令随后是否报告错误。

    • 一个简单的例子:Invoke-Expression '1 / 0'$? 设置为 $True(!),因为 Invoke-Expression 能够解析并 调用 表达式,即使表达式本身失败了。
    • 同样,检查 $Error 集合会告诉您目标命令是否报告了错误以及报告了哪些错误。
  • 使用 远程处理(总是间接执行)cmdlet,特别是 Invoke-Command-ComputerName 参数(这是典型的),而且还带有隐式远程处理 cmdlet,$? 确实 反映了 目标命令 是否报告任何错误。

    • 一个简单的例子(必须是来自 提升的 控制台的 运行 并且假定本地计算机已经设置为远程处理):
      Invoke-Command -ComputerName . { 1 / 0 },因为涉及remoting,确实将$?设置为$False以反映目标命令1 / 0.[=152的失败=] 请注意,即使本地计算机 (.) 是目标,使用 -ComputerName 也总是使用远程处理。

    • 请注意,根据设计,远程报告通常 终止 远程发生的错误 非终止 错误,据推测,一台目标机器上的正常终止错误不会中止所有其他机器上的处理。


  • 反映 $? 中错误的命令示例:

    # 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('>')
    
  • 不反映 $? 中错误的命令示例:

    <#
      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 }