Col*_*nic 43 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
Run Code Online (Sandbox Code Playgroud)
没有惊喜.我打印一条消息到标准错误(使用cmd
's 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
Run Code Online (Sandbox Code Playgroud)
我的第一个问题,为什么NativeCommandError?
其次,为什么在成功运行$?
时为False 为0?PowerShell 关于自动变量的文档没有明确定义.我总是认为它是真的,当且仅当它是0,但我的例子与之相矛盾.cmd
$LastExitCode
$?
$LastExitCode
这是我在现实世界(简化)中遇到这种行为的方式.真的是FUBAR.我从另一个调用了一个PowerShell脚本.内部脚本:
cmd /c "echo Hello from standard error 1>&2"
if (! $?)
{
echo "Job failed. Sending email.."
exit 1
}
# Do something else
Run Code Online (Sandbox Code Playgroud)
简单地运行.\job.ps1
它,它工作正常,并且不发送电子邮件.但是,我从另一个PowerShell脚本调用它,记录到一个文件.\job.ps1 2>&1 > log.txt
.在这种情况下,会发送一封电子邮件!使用错误流在脚本外部执行的操作会影响脚本的内部行为.观察现象会改变结果.这感觉像量子物理而不是脚本!
[有趣的是:.\job.ps1 2>&1
取决于你在哪里运行,可能会或不会爆炸]
Dro*_*roj 74
(我使用的是PowerShell v2.)
' $?
'变量记录在about_Automatic_Variables
:
$? Contains the execution status of the last operation
这是指最近的PowerShell操作,而不是最后一个外部命令,这是你得到的$LastExitCode
.
在您的示例中,$LastExitCode
为0,因为最后一个外部命令是cmd
,它成功地回显了一些文本.但是,2>&1
导致邮件stderr
要转换的输出流,它告诉PowerShell中,有过去的过程中出现错误的错误记录的操作,导致$?
要False
.
为了进一步说明这一点,请考虑以下事项:
> java -jar foo; $?; $LastExitCode Unable to access jarfile foo False 1
$LastExitCode
是1,因为这是java.exe的退出代码.$?
是假的,因为shell失败的最后一件事.
但如果我所做的只是切换它们:
> java -jar foo; $LastExitCode; $? Unable to access jarfile foo 1 True
...然后$?
是真的,因为shell做的最后一件事是打印$LastExitCode
到主机,这是成功的.
最后:
> &{ java -jar foo }; $?; $LastExitCode Unable to access jarfile foo True 1
...这似乎有点反直觉,但现在$?
是True,因为脚本块的执行是成功的,即使在其中运行的命令不是.
返回2>&1
重定向....导致错误记录进入输出流,这就是那个长篇大论的blob NativeCommandError
.shell正在转储整个错误记录.
这可能是特别恼人时,所有你需要做的就是管stderr
和 stdout
在一起,这样他们就可以在日志文件或东西相结合.谁想要PowerShell对接他们的日志文件??? 如果我这样做ant build 2>&1 >build.log
,那么任何错误都会stderr
导致PowerShell的nosy $ 0.02,而不是在我的日志文件中获得干净的错误消息.
但是,输出流不是文本流!重定向只是对象管道的另一种语法.错误记录是对象,因此您需要做的就是在重定向之前将该流上的对象转换为字符串:
从:
> 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
Col*_*nic 16
这个错误是PowerShell针对错误处理的规范性设计无法预料的结果,因此很可能永远不会修复它.如果您的脚本仅与其他PowerShell脚本一起播放,那么您就是安全的.但是,如果您的脚本与来自广阔世界的应用程序进行交互,则此错误可能会受到影响.
PS> nslookup microsoft.com 2>&1 ; echo $?
False
Run Code Online (Sandbox Code Playgroud)
疑难杂症!尽管如此,经过一些痛苦的刮擦,你永远不会忘记这一课.
($LastExitCode -eq 0)
而不是$?
Joe*_*oey 10
(注意:这主要是推测;我很少在PowerShell中使用很多本机命令,而其他人可能比我更了解PowerShell内部)
我猜您在PowerShell控制台主机中发现了差异.
NativeCommandError
.2>&1
重定向运算符如何都会失败的原因.2>&1
因为标准错误流上的输出必须重定向并因此被读取.我的猜测是,控制台PowerShell主机是懒惰的,如果它不需要对其输出进行任何处理,只需手动本机控制台命令控制台.
我真的相信这是一个错误,因为PowerShell的行为会有所不同,具体取决于主机应用程序.
mkl*_*nt0 10
更新:v7.2 的预览版本现在包含一个实验性功能,可修复以下问题 - 请参阅此答案;但是请注意,实验性功能不能保证成为官方功能。
v7.1的问题总结:
PowerShell 引擎仍然存在与2>
应用于外部程序调用的重定向相关的错误:
根本原因是using2>
导致 stderr(标准错误)输出通过 PowerShell 的错误流(请参阅about_Redirection)进行路由,这会产生以下不良后果:
如果$ErrorActionPreference = 'Stop'
碰巧生效,使用2>
意外触发脚本终止错误,即中止脚本(即使在形式中2>$null
,其意图显然是忽略stderr 行)。请参阅此 GitHub 问题。
$ErrorActionPreference = 'Continue'
由于2>
当前涉及错误流,如果至少发出一个 stderr 行$?
,则自动成功状态变量总是设置为$False
,然后不再反映命令的真实成功状态。请参阅此 GitHub 问题。
$LASTEXITCODE -eq 0
用于在调用外部程序后测试是否成功。使用2>
,stderr 行意外地记录在自动$Error
变量中(该变量保存会话中发生的所有错误的日志) - 即使您使用2>$null
. 请参阅此 GitHub 问题。
$Error.RemoveAt()
一一删除它们之外,没有。一般情况下,不幸的是,一些PowerShell的主机默认情况下,通过PowerShell的错误流从外部程序输出的路线标准错误,即把它当作错误输出,这是不恰当的,因为很多外部程序使用标准错误也为状态信息,或者更一般地,这通常就是不是数据(git
作为主要示例):并非每个 stderr 行都可以假设表示错误,并且 stderr 输出的存在并不意味着失败。
受影响的主机:
该过时的Windows PowerShell ISE和其它可能的,旧的基于GUI的IDE比其他Visual Studio代码。
当执行通过外部程序PowerShell远程或在后台作业(这两个调用机制共享相同的基础设施和使用ServerRemoteHost
主机附带的PowerShell)。
确实在非远程、非后台调用中按预期运行的主机(它们将 stderr 行传递到显示器并正常打印它们):
终端(控制台),包括Windows 终端。
带有PowerShell 扩展的Visual Studio Code;此跨平台编辑器 (IDE) 旨在取代 Windows PowerShell ISE。
此GitHub 问题 中讨论了主机间的这种不一致。
归档时间: |
|
查看次数: |
28055 次 |
最近记录: |