【问题标题】:powershell 2.0 redirection file handle exceptionpowershell 2.0 重定向文件句柄异常
【发布时间】:2012-02-17 04:21:13
【问题描述】:

我正在寻找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. 异常的解决方案,该解决方案也适用于在包含"the fix" 的脚本中调用的脚本。

就这个问题而言,假设我有两个脚本:

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

bar.ps1

write-host "normal"
write-error "error"
write-host "yay"

foo.ps1 是这样运行的:

powershell .\foo.ps1 > C:\temp\redirecct.log 2>&1

预期的输出应该是:

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

但是,由于已知的错误,输出实际上是:

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

所以观察到的行为是“修复”所做的更改没有被“子”脚本继承(bar.ps1,在这种情况下)。当 bar.ps1 尝试写入时,它会严重崩溃。如果我在foo.ps1 中不以某种方式防范它,它也会严重崩溃。在调用bar.ps1 之前/调用中我可以做些什么来防止bar.ps1 在尝试写入时崩溃?

约束:

  • Powershell 2.0
  • 脚本必须按上述方式运行
  • 我无法修改 bar.ps1(写入 stderr 时它不应该崩溃)。

更新

下面是一个可以接受的解决方案。我说一半是因为它只能防止“父”脚本崩溃。 '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"

【问题讨论】:

    标签: windows powershell command-line file-io powershell-2.0


    【解决方案1】:

    如果你在 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
    

    通过 more 管道输出隐藏了这样一个事实,即它最终会从 Powershell 的子实例转到文件,从而绕过错误。

    事实上,如果你创建一个只运行 foo.ps1 的祖父脚本 foobar.ps1:

    powershell .\foo.ps1 2>&1 | more
    

    那么你根本不需要“修复”,而 foo.ps1 就可以了

    write-host "normal"
    write-error "error"
    write-host "yay"
    
    .\bar.ps1
    

    因为管道解决了所有后代脚本的问题。

    【讨论】:

    • 注意: foobar.ps1 应该在调用 foo.ps1 之前检查输出是否被重定向;如果它没有被重定向,它可能不应该被传送到更多。
    • 感谢您的回答!我今天实际上尝试了一个通用父包装脚本的想法。不过,我发现了类似 `$output = invoke-command "$args 2>&1" 之类的成功(从我上面展示的内容扩展而来),而不是更多。另外,我感谢您查看所有 3 个未解决的问题 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-19
    • 2021-08-18
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    相关资源
    最近更新 更多