为什么PowerShell在下面的第二个示例中显示出令人惊讶的行为?

首先,一个理智的举动:

PS C:\> & cmd /c "echo Hello from standard error 1>&2"; echo "`$LastExitCode=$LastExitCode and `$?=$?"
Hello from standard error
$LastExitCode=0 and $?=True

没什么好奇怪的我将消息打印为标准错误(使用cmdecho)。我检查变量$?$LastExitCode。正如预期的那样,它们分别等于True和0。

但是,如果我要求PowerShell通过第一个命令将标准错误重定向到标准输出,则会收到NativeCommandError:
PS C:\> & cmd /c "echo Hello from standard error 1>&2" 2>&1; echo "`$LastExitCode=$LastExitCode and `$?=$?"
cmd.exe : Hello from standard error
At line:1 char:4
+ cmd <<<<  /c "echo Hello from standard error 1>&2" 2>&1; echo "`$LastExitCode=$LastExitCode and `$?=$?"
    + CategoryInfo          : NotSpecified: (Hello from standard error :String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

$LastExitCode=0 and $?=False

我的第一个问题,为什么要使用NativeCommandError?

其次,当$?成功运行且cmd为0时,为什么$LastExitCode为False? PowerShell的文档about automatic variables没有明确定义$?。我总是认为且仅当$LastExitCode为0时,它才为True,但我的示例与此矛盾。

这是我在现实世界中(简化)遇到的这种行为的方式。真的是FUBAR。我正在从另一个调用一个PowerShell脚本。内部脚本:
cmd /c "echo Hello from standard error 1>&2"
if (! $?)
{
    echo "Job failed. Sending email.."
    exit 1
}
# Do something else

只需将其作为.\job.ps1来运行,就可以正常工作,并且不会发送电子邮件。但是,我是从另一个PowerShell脚本调用的,并记录到文件.\job.ps1 2>&1 > log.txt。在这种情况下,将发送电子邮件!您在脚本外部执行的错误流会影响脚本的内部行为。观察现象会改变结果。感觉就像是量子物理学,而不是脚本!

[有趣的是:取决于运行的位置,.\job.ps1 2>&1可能不会爆炸]

最佳答案

(我正在使用PowerShell v2。)

'$?'变量记录在about_Automatic_Variables中:

$?
包含上一个操作的执行状态

这是指最新的PowerShell操作,而不是最后一个外部命令,即$LastExitCode中得到的命令。

在您的示例中,$LastExitCode为0,因为最后一个外部命令是cmd,它成功地回显了一些文本。但是2>&1导致将消息stderr转换为输出流中的错误记录,这告诉PowerShell上一次操作期间出现错误,导致$?False

为了进一步说明这一点,请考虑以下问题:

> java -jar foo; $ ?; $ LastExitCode
无法访问jarfile foo
错误的
1个
$LastExitCode为1,因为那是java.exe的退出代码。 $?为False,这是因为Shell所做的最后一件事失败了。

但是,如果我要做的就是切换它们:

> java -jar foo; $ LastExitCode; $?
无法访问jarfile foo
1个
真的

...然后$?为True,因为 shell 所做的最后一件事是将$LastExitCode打印到主机,这成功了。

最后:

>&{java -jar foo}; $ ?; $ LastExitCode
无法访问jarfile foo
真的
1个

...这似乎有点违反直觉,但是$?现在为True,因为脚本块的执行成功,即使命令在其内部运行也不成功。

返回到2>&1 redirect ....导致错误记录进入输出流,这就是有关NativeCommandError的冗长 Blob 。 Shell正在转储整个错误记录。

当您要做的只是将stderrstdout管道在一起以便可以将它们组合到日志文件或其他内容中时,这可能会特别烦人。谁想要PowerShell对接他们的日志文件???如果我执行ant build 2>&1 >build.log,那么到stderr的任何错误都将使用PowerShell的$ 0.02香,而不是在我的日志文件中获取干净的错误消息。

但是,输出流不是文本流!重定向只是对象管道的另一种语法。错误记录是对象,因此您所要做的就是在重定向之前将该流上的对象转换为字符串:

从:

> cmd/c“从标准错误1>&2回显Hello” 2>&1
cmd.exe:来自标准错误的您好
行:1字符:4
+ cmd&2“2>&1
+ CategoryInfo:未指定:(来自标准错误的Hello:String)[],RemoteException
+ FullyQualifiedErrorId:NativeCommandError

到:

> cmd/c“从标准错误1>&2回显Hello” 2>&1 | %{“$ _”}
您好,标准错误

...并重定向到文件:

> cmd/c“从标准错误1>&2回显Hello” 2>&1 | %{“$ _”} |发球out.txt
您好,标准错误

...要不就:

> cmd/c“从标准错误1>&2回显Hello” 2>&1 | %{“$ _”}> out.txt

关于windows - $ LastExitCode = 0,但在PowerShell中$?= False。将stderr重定向到stdout会给出NativeCommandError,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10666101/

10-09 00:15