为什么 PowerShell 不处理这个 $?退出代码正确?

Why is PowerShell not treating this $? exit code correctly?

我想将 PowerShell 脚本编写到 select JFrog 服务连接,但脚本的行为方式与我期望的不同。

当我手动 运行 jfrog config use 命令时,它按预期工作:

PS C:\Users\rdepew> jfrog config use Dummy
[Info] Using server ID 'Dummy' (https://foo.jfrog.io/).
PS C:\Users\rdepew> $?
True

我希望能够使用始终 returns true 或 false 的 $? 来检测 jfrog config use 是否已成功执行。这个片段说明了我遇到的问题:

PS C:\Users\rdepew> if (jfrog config use Dummy) {
>>   Write-Host Dummy exists
>> } else {
>>   Write-Host Dummy doesnt exist but I dont believe it
>> }
[Info] Using server ID 'Dummy' (https://foo.jfrog.io/).
Dummy doesnt exist but I dont believe it

[Info] 行表明 JFrog 实际上正在使用服务器连接 'Dummy'。我假设 $?退出代码评估为真,就像我手动执行时一样。但是我的 if/else 测试采用了“if false”分支。为什么?

更多信息:如果我使用不存在的服务连接的名称,我会得到预期的行为:

PS C:\Users\rdepew> if (jfrog config use Bogus) {
>>   Write-Host Bogus exists
>> } else {
>>   Write-Host Bogus doesnt exist
>> }
[Error] Could not find a server with ID 'Bogus'.
Bogus doesnt exist

我不明白为什么 PS 对于 True 和 False 的情况都跳转到 else 子句。

更多信息

感谢第一位的回答和评论,让我明白了一些。我可以这样重写我的代码片段:

jfrog config add Dummy
if ($LASTEXITCODE=0) {
  etc.

但我仍然对 $?行为。从命令行,$?行为如下:

PS C:\Users\rdepew> jfrog config use Dummy
[Info] Using server ID 'Dummy' (https://foo.jfrog.io/).
PS C:\Users\rdepew> $?
True
PS C:\Users\rdepew> jfrog config use Bogus
[Error] Could not find a server with ID 'Bogus'.
PS C:\Users\rdepew> $?
False

...这是人们所期望的。

在 PowerShell 中,$? returns $true 如果之前的 cmdlet 没有错误地完成,或者 $false 如果它出现任何错误。对于外部命令,它将return $LASTEXITCODE -eq 0.

的结果

但是,当您将命令(外部或其他)作为 if 语句中的条件时,它不会检查命令是否成功,而是检查结果的真实性。如果命令 return 是适当的 $true$false 布尔值,这很简单,否则,它将成功流上的输出转换为布尔值。除以下输出之外的任何输出的计算结果为 $true$null0、空字符串和空数组的计算结果为 $false.

但我不确定的是为什么两个命令至少在 运行 作为 if 条件时不产生 $true 输出。另一种编写代码的方法是在 if 之外 运行 jfrog 并检查 $LASTEXITCODE:

的结果
$output = jfrog config use Dummy

if ( $LASTEXITCODE -eq 0 ) {
  Write-Host "Dummy exists"
} else {
  Write-Host "Dummy doesnt exist but I dont believe it"
}

如果可以接受其他退出代码,请使用这个奇怪的技巧来检查多个值,而不必将 $LASTEXITCODE 的多个 eval 链接在一起:

# I use this often when checking the result of MSIEXEC
if ( $LASTEXITCODE -in @(0, 3010) ) {
....
}

基本上,这只是说要检查 $LASTEXITCODE 是否存在于 -in 右侧的数组中。您可以使用 -notin 来否定评估。 -contains/-notcontains 运算符也是一个选项,您只需反转 -in/-notin.

的操作数

经过一些额外的测试,并模糊地回忆起几年前工作中的这个问题,我相信 jfrog 正在将所有内容写入 STDERR,这将解释您的行为(您可以使用 $var = jfrog blah blah blah 进行测试并查看 $var 是否包含输出或者它是否仍被写入控制台)。

如果您想减轻这种情况,例如,如果您想捕获输出,请将错误流(基本上是 STDERR)重定向到成功流(基本上是 STDOUT):

$output = jfrog blah blah blah 2>&1

但是,我仍然会依靠检查 $LASTEXITCODE 来检查失败而不是输出或 $?。检查此问题中出现的类似问题的输出结果,并检查 $? 需要您注意将来对代码的更新,因为您可以很容易地在执行和结果评估之间插入另一个命令。

I have a comprehensive answer here 关于不同的输出流和一般的重定向,您可能会发现它们很有用。