Windows $LastExitCode=0,但PowerShell中的$?=False。将stderr重定向到stdout会产生NativeCommandError
为什么PowerShell在下面的第二个示例中表现出令人惊讶的行为 首先,一个理智行为的例子:Windows $LastExitCode=0,但PowerShell中的$?=False。将stderr重定向到stdout会产生NativeCommandError,windows,powershell,command-line,Windows,Powershell,Command Line,为什么PowerShell在下面的第二个示例中表现出令人惊讶的行为 首先,一个理智行为的例子: PS C:\> & cmd /c "echo Hello from standard error 1>&2"; echo "`$LastExitCode=$LastExitCode and `$?=$?" Hello from standard error $LastExitCode=0 and $?=True Enable-ExperimentalFeature PSN
PS C:\> & cmd /c "echo Hello from standard error 1>&2"; echo "`$LastExitCode=$LastExitCode and `$?=$?"
Hello from standard error
$LastExitCode=0 and $?=True
Enable-ExperimentalFeature PSNotApplyErrorActionToStderr
没有意外。我将消息打印为标准错误(使用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
只需将其作为\job.ps1
运行,即可正常工作,并且不会发送电子邮件。但是,我是从另一个PowerShell脚本调用它的,记录到一个文件\job.ps1 2>&1>log.txt
。在这种情况下,会发送一封电子邮件!使用错误流在脚本外部执行的操作会影响脚本的内部行为。观察一种现象会改变结果。这感觉像量子物理,而不是脚本
[有趣的是:\job.ps1 2>&1
可能会爆炸,也可能不会爆炸,这取决于您运行它的位置](注意:这主要是猜测;我很少在PowerShell中使用许多本机命令,其他人可能比我更了解PowerShell内部)
我猜您在PowerShell控制台主机中发现了差异
NativeCommandError
2>&1
重定向操作符,此操作都会失败2>&1
重定向操作符,控制台主机将监视标准错误流,因为必须重定向并读取标准错误流上的输出我真的认为这是一个错误,因为PowerShell的行为因主机应用程序而异。此错误是PowerShell规范性错误处理设计的一个不可预见的后果,因此很可能永远无法修复。如果您的脚本仅与其他PowerShell脚本一起使用,则您是安全的。但是,如果您的脚本与来自世界各地的应用程序交互,那么这个bug可能会咬人
PS> nslookup microsoft.com 2>&1 ; echo $?
False
明白了!尽管如此,在痛苦的抓挠之后,你永远不会忘记这一课
使用($LastExitCode-eq 0)
而不是$?
(我使用的是PowerShell v2。)
“$?
”变量记录在关于自动变量的中:
$?
Contains the execution status of the last operation
$LastExitCode
是1,因为那是java.exe的退出代码$?
为False,因为shell所做的最后一件事失败了
但如果我所做的只是把它们换过来:
> java -jar foo; $LastExitCode; $?
Unable to access jarfile foo
1
True
…这似乎有点违反直觉,但现在是真的,因为脚本块的执行是成功的,即使在其中运行的命令不是成功的
返回到2>&1
重定向。。。。这会导致错误记录进入输出流,这就是关于NativeCommandError
的冗长blob的原因。shell正在转储整个错误记录
当您只想通过管道将stderr
和stdout
组合在一起,以便将它们组合到日志文件或其他文件中时,这可能会特别烦人。谁想让PowerShell连接到他们的日志文件???如果我执行了ant build 2>&1>build.log
,那么任何转到stderr
的错误都会添加PowerShell的nosey$0.02,而不是在我的日志文件中获得干净的错误消息
但是,输出流不是文本流!重定向只是对象管道的另一种语法。错误记录是对象,因此您只需在重定向之前将该流上的对象转换为字符串:
发件人:
>cmd/c“echo Hello from standard error 1>&2”2>&1
cmd.exe:Hello from standard error
第1行字符数:4
+cmd&2“2>&1
+CategoryInfo:NotSpecified:(来自标准错误的Hello:String)[],RemoteException
+FullyQualifiedErrorId:NativeCommandError
致:
>cmd/c“echo Hello from standard error 1>&2”2>&1 |%{“$"”}
来自标准错误的您好
…并通过重定向到文件:
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{ "$_" } | tee out.txt
Hello from standard error
>cmd/c“echo Hello from standard error 1>&2”2>&1 |%{“$|”}| tee out.txt
来自标准错误的您好
…或者只是:
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{ "$_" } >out.txt
>cmd/c“echo Hello from standard error 1>&2”2>&1 |%{“$"}>out.txt
对我来说,这是一个错误操作偏好的问题。
当从ISE运行时,我在第一行中设置了$ErrorActionPreference=“Stop”,这就是截取所有事件,并将*>&1作为参数添加到调用中
所以首先我有一句话:
&$exe$parameters*>&1
正如我所说,这不起作用,因为我之前在文件中有$ErrorActionPreference=“Stop”(或者可以在概要文件中全局设置,以便用户启动脚本)
因此,我尝试将其包装在调用表达式中以强制执行ErrorAction:
Invoke Expression-Command“&`“$exe`”$parameters*>&1”-错误操作继续
这也不管用
因此,我不得不回过头来使用临时覆盖的ErrorActionPreference进行黑客攻击:
$old\u error\u action\u preference=$ErrorActionPreference
尝试
{
$ErrorActionPreference=“继续”
&$exe$parameters*>&1
}
最后
{
$ErrorActionPreference=$old\u error\u action\u preference
}
这对我有用
我把它包装成一个函数:
功能启动NativeExecutable
{
[CmdletBinding(SupportsShouldProcess=$true)]
Param
(
[参数(强制)=
> cmd /c "echo Hello from standard error 1>&2" 2>&1
cmd.exe : Hello from standard error
At line:1 char:4
+ cmd &2" 2>&1
+ CategoryInfo : NotSpecified: (Hello from standard error :String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{ "$_" }
Hello from standard error
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{ "$_" } | tee out.txt
Hello from standard error
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{ "$_" } >out.txt
Enable-ExperimentalFeature PSNotApplyErrorActionToStderr
& cmd /c "echo Hello from standard error 1>&2" 2>&1
echo "`$LastExitCode=$LastExitCode and `$?=$?"
echo "`$Error.Count=$($Error.Count)"