Chr*_*lan 7 windows powershell file-io command-line powershell-2.0
我正在寻找一个The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream.
异常的解决方案,该解决方案也适用于包含"修复"的脚本中调用的脚本.
出于这个问题的目的,假设我有两个脚本:
foo.ps1
# <fix>
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$objectRef = $host.GetType().GetField( "externalHostRef", $bindingFlags ).GetValue( $host )
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetProperty"
$consoleHost = $objectRef.GetType().GetProperty( "Value", $bindingFlags ).GetValue( $objectRef, @() )
[void] $consoleHost.GetType().GetProperty( "IsStandardOutputRedirected", $bindingFlags ).GetValue( $consoleHost, @() )
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$field = $consoleHost.GetType().GetField( "standardOutputWriter", $bindingFlags )
$field.SetValue( $consoleHost, [Console]::Out )
$field2 = $consoleHost.GetType().GetField( "standardErrorWriter", $bindingFlags )
$field2.SetValue( $consoleHost, [Console]::Out )
# </fix>
write-host "normal"
write-error "error"
write-host "yay"
.\bar.ps1
Run Code Online (Sandbox Code Playgroud)
bar.ps1
write-host "normal"
write-error "error"
write-host "yay"
Run Code Online (Sandbox Code Playgroud)
而foo.ps1
正在运行的是这样的:
powershell .\foo.ps1 > C:\temp\redirecct.log 2>&1
Run Code Online (Sandbox Code Playgroud)
预期产量应为:
normal
C:\foo.ps1 : error
At line:1 char:10
+ .\foo.ps1 <<<<
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,foo.ps1
yay
normal
C:\bar.ps1 : error
At C:\foo.ps1:17 char:6
+ .\bar <<<< 2>&1
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,bar.ps1
yay
Run Code Online (Sandbox Code Playgroud)
但是,由于已知错误,输出实际上是:
normal
C:\foo.ps1 : error
At line:1 char:10
+ .\foo.ps1 <<<<
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,foo.ps1
yay
normal
out-lineoutput : The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win3
2 code or another FileStream. This may cause data loss.
+ CategoryInfo : NotSpecified: (:) [out-lineoutput], IOException
+ FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.OutLineOutputCommand
Run Code Online (Sandbox Code Playgroud)
因此,观察到的行为是"修复"所做的更改不会被"子"脚本继承(bar.ps1
在本例中).当bar.ps1尝试写入时,它会崩溃.如果我不以某种方式防范它foo.ps1
,它也会崩溃.在调用之前/我可以做什么,bar.ps1
以防止bar.ps1
在尝试写入时崩溃?
约束:
UPDATE
以下是半可接受的解决方案.我说一半因为它只会阻止'父'脚本崩溃.'child'脚本在尝试写入时仍然很难.从好的方面来说,它可以认识到酒吧失败了.
foo.ps1:
function savepowershellfromitself {
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$objectRef = $host.GetType().GetField( "externalHostRef", $bindingFlags ).GetValue( $host )
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetProperty"
$consoleHost = $objectRef.GetType().GetProperty( "Value", $bindingFlags ).GetValue( $objectRef, @() )
[void] $consoleHost.GetType().GetProperty( "IsStandardOutputRedirected", $bindingFlags ).GetValue( $consoleHost, @() )
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$field = $consoleHost.GetType().GetField( "standardOutputWriter", $bindingFlags )
$field.SetValue( $consoleHost, [Console]::Out )
$field2 = $consoleHost.GetType().GetField( "standardErrorWriter", $bindingFlags )
$field2.SetValue( $consoleHost, [Console]::Out )
}
savepowershellfromitself
write-host "normal"
write-error "error"
write-host "yay"
$output = .\bar.ps1 2>&1
savepowershellfromitself
write-host "$output"
if( $errors = $output | ?{$_.gettype().Name -eq "ErrorRecord"} ){
write-host "there were errors in bar!"
}
write-error "error2"
write-host "done"
Run Code Online (Sandbox Code Playgroud)
如果您在 foo.ps1 中执行此操作,则可以解决问题:
# <fix>
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$objectRef = $host.GetType().GetField( "externalHostRef", $bindingFlags ).GetValue( $host )
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetProperty"
$consoleHost = $objectRef.GetType().GetProperty( "Value", $bindingFlags ).GetValue( $objectRef, @() )
[void] $consoleHost.GetType().GetProperty( "IsStandardOutputRedirected", $bindingFlags).GetValue( $consoleHost, @() )
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$field = $consoleHost.GetType().GetField( "standardOutputWriter", $bindingFlags )
$field.SetValue( $consoleHost, [Console]::Out )
$field2 = $consoleHost.GetType().GetField( "standardErrorWriter", $bindingFlags )
$field2.SetValue( $consoleHost, [Console]::Out )
# </fix>
write-host "normal"
write-error "error"
write-host "yay"
powershell .\bar.ps1 2>&1 | more
Run Code Online (Sandbox Code Playgroud)
通过 more 管道输出隐藏了这样一个事实:它最终会从 Powershell 的子实例发送到文件,从而绕过该错误。
事实上,如果您创建一个仅运行 foo.ps1 的祖父母脚本 foobar.ps1:
powershell .\foo.ps1 2>&1 | more
Run Code Online (Sandbox Code Playgroud)
那么你根本不需要“修复”,并且 foo.ps1 可以是
write-host "normal"
write-error "error"
write-host "yay"
.\bar.ps1
Run Code Online (Sandbox Code Playgroud)
因为管道解决了所有后代脚本的问题。