在Powershell中,如何设置$?不将此值发送到管道?

Posted

技术标签:

【中文标题】在Powershell中,如何设置$?不将此值发送到管道?【英文标题】:In Powershell, how to set $? without sending this value to the pipeline? 【发布时间】:2022-01-22 23:04:39 【问题描述】:
PS> gci C:\
<<here it doesn't print the value of $? as 'true' after command executes>

PS> $?
true

但是,当我尝试使用自己的 powershell 函数重新创建此行为时,我得到:

PS> function test1  write-host "hello"; return $true

PS> test1
hello
true

PS> $?
true

什么给了?为什么我不能从管道中隐藏函数的返回值,并且只将它写入 $?变量?

【问题讨论】:

试试function test1 write-host "hello"; 你想解决什么问题? $?包含最后一个命令的执行状态。这里实际上没有问题。如果你想看到它是假的,你可以尝试类似gci some/random/path/,然后你可以输入$?,你会遇到它是假的 你也可以使用Write-Verbose让你的命令正常运行,但是如果你需要通过添加-Verbose来显示一些有用的输出。 【参考方案1】:

这是 PowerShell 中的许多自动变量之一,您可以通过运行查看完整列表:

get-help about_Automatic_Variables

$? 的作用是查看最后一条命令是否运行完成或遇到未捕获的错误。

例如

function Throw-WhenOdd($value)
   if ($value % 2 -ne 0)
      throw "OddNumber"
   



PS> Throw-WhenOdd 4
PS> $?
True
PS> Throw-WhenOdd 3
Exception:
Line |
   3 |        throw "OddNumber"
     |        ~~~~~~~~~~~~~~~~~
     | OddNumber
PS>$?
False
PS> #Checking the value of `$?` resets the value
# so checking a second time returns true
PS> $?
true

总而言之,如果您有一个函数或 cmdlet 或其他可以抛出的东西,您可以使用$? 来查看它是否抛出。但是,您必须使用 Throw 而不是 Write-Error,并且在您第一次检查 $? 时会消耗输出。

【讨论】:

这很有趣...我没有意识到交互式 shell 捕获了一个异常来设置 $?。我认为这再次证明了在 powershell 脚本中应该避免“返回”,因为它会向你的终端打印你不想看到的废话......我发现从函数返回值的唯一方法是传递在散列中并将返回值设置为散列元素。那么它很好地不打印返回值。 @pico 或者你可以返回你本来会写出来的东西——在上面的例子中,'hello' Pico,这是对函数的完全有效使用,将它们传递给方法并切换它们的值。这是 dotnet 将变量作为引用传递的方式(这会导致实际且永久地更改输入对象) 虽然throw 语句确实会导致$? 反映$false 如果运行空间保持活动状态,但重要的是要注意throw 生成一个 script-terminating (runspace-terminating) 错误,所以如果你调用一个使用throw的函数从一个脚本内部,你必须使用try/@ 987654332@ 以防止脚本立即终止,然后才能查询$? 并且只能在catch 块的开头。【参考方案2】:

首先,澄清一下:

在 PowerShell 中,return - 如果给定一个参数 - 将该参数输出到成功输出流,即它返回 data - 就像 implicit 输出或带有Write-Output 的输出(很少需要使用)。

另请注意,Write-Host不是设计用于输出数据 - 它设计用于直接到主机(控制台)输出 - 请参阅this answer。

相比之下,在bash 等与POSIX 兼容的shell 中,returnexit 的函数级模拟,并设置了一个不可见的退出代码

PowerShell 的$? 是一个抽象错误条件指示器$true 如果没有发生错误,$false 否则),由 PowerShell 自身自动设置指示最近执行的命令或表达式是否遇到错误情况 - 请参阅下面的详细信息。


从 PowerShell 7.2 开始,您无法设置automatic $? variable;它是由 PowerShell 本身设置的 - 请参阅底部了解详细信息。

GitHub issue #10917 已认识到需要直接设置 $? 并获得批准,但尚未实现。

只能间接设置

通过退出代码,仅适用于外部程序(进程退出代码)脚本文件 (*.ps1) 如果它们以exit 语句终止。

如果是0,则最近报告的退出代码(也反映在automatic $LASTEXITCODE variable 中)映射到报告$true$?,以及对于任何非零值的$false

对于 cmdlet函数,通过输出到 PowerShell 的错误流

对于 二进制 cmdlet,发出至少一个 非终止 错误或 语句终止错误 会导致 $? 反映 @987654353 @

从 PowerShell 7.2 开始,(根据定义为写在 PowerShell 中)函数 仅影响 $?,如果它们是 高级 函数并使用笨重的 $PSCmdlet.WriteError() (对于非终止错误;参见this answer)$PSCmdlet.ThrowTerminatingError()(对于语句终止错误)方法:

Write-Error(对于非终止错误),不幸的是,会影响$? - 请参阅GitHub issue #3629

虽然throw statement - 如FoxDeploy's answer 所示 - 确实 最终将$? 设置为$false,但它会生成一个脚本-终止(运行空间-terminating) 错误,要求调用者显式捕获错误,使用try / catch,然后必须在catch 块的开头查询$?

值得注意的是,上面的意思是:

$? 反映$false 总是需要产生错误输出$?$false 只是告诉您发生了 一些 错误 - 这并不是表明命令成功与失败的明确意图作为一个整体.

【讨论】:

喜欢关于为什么Write-Error 不会影响$? 的说明。恕我直言,这对我来说仍然感觉不对 谢谢,@FoxDeploy - 我同意这是错误的。这个问题在GitHub issue #3629 中讨论(我已经更新了链接到它的答案)。

以上是关于在Powershell中,如何设置$?不将此值发送到管道?的主要内容,如果未能解决你的问题,请参考以下文章

powershell psobject显示为字符串而不是psobject,如何转换回psobject

我如何将此值存储到数据库中[关闭]

使用 Exists 或 Select 将此值设置为位变量哪个更好?

在 Powershell 中,如何通过提供名称作为 $var 来设置环境变量?

如何使用 moment.js 将此值 theHour[0].times 更改为时间格式 (HH:mm)

一个 PowerShell SSH 可以不将密钥存储在磁盘上吗?