为什么PowerShell在下面的第二个示例中显示出令人惊讶的行为?
首先,一个理智的举动:
PS C:\> & cmd /c "echo Hello from standard error 1>&2"; echo "`$LastExitCode=$LastExitCode and `$?=$?"
Hello from standard error
$LastExitCode=0 and $?=True
没什么好奇怪的我将消息打印为标准错误(使用
cmd
的echo
)。我检查变量$?
和$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正在转储整个错误记录。
当您要做的只是将stderr
和stdout
管道在一起以便可以将它们组合到日志文件或其他内容中时,这可能会特别烦人。谁想要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/