2012-01-23 37 views
7

Tôi đang tìm giải pháp cho ngoại lệ 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. cũng sẽ hoạt động trên các tập lệnh được gọi trong tập lệnh có chứa "the fix".powershell 2.0 tệp chuyển hướng xử lý ngoại lệ

Theo mục đích của câu hỏi này, nói rằng tôi có hai kịch bản:

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 đang được chạy như thế này:

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

Sản lượng dự kiến ​​nên là:

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 

Tuy nhiên, do các lỗi được biết, sản lượng thực sự là:

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 

Vì vậy, các hành vi quan sát được là những thay đổi được thực hiện bởi " bản sửa lỗi "không được thừa hưởng bởi tập lệnh 'con' (bar.ps1, trong trường hợp này). Khi bar.ps1 cố gắng viết, nó bị treo cứng. Nếu tôi không bảo vệ chống lại nó bằng cách nào đó trong foo.ps1, nó cũng sẽ sụp đổ cứng. Tôi có thể làm gì trước/gọi bar.ps1 để ngăn không cho bar.ps1 bị lỗi khi cố gắng viết?

ràng buộc:

  • Powershell 2.0
  • Các kịch bản phải được chạy như trên
  • tôi không thể sửa đổi bar.ps1 (và nó không nên sụp đổ khi viết để stderr).

CẬP NHẬT

Dưới đây là một giải pháp nửa chấp nhận được. Tôi nói một nửa vì nó chỉ ngăn chặn kịch bản 'cha mẹ' bị rơi. Kịch bản 'con' vẫn không thành công khi nó cố viết. Về phía cộng, nó có thể đi xa như nhận ra rằng thanh thất bại.

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" 

Trả lời

1

Nếu bạn làm điều này trong foo.ps1 nó giải quyết vấn đề:

# <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 

Piping đầu ra thông qua nhiều da thực tế là nó là cuối cùng đi vào một tập tin từ thể hiện con của Powershell, bỏ qua lỗi.

Trong thực tế, nếu bạn tạo một kịch bản foobar.ps1 ông bà mà chỉ chạy foo.ps1:

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

Sau đó, bạn không cần phải "sửa chữa" chút nào, và foo.ps1 chỉ có thể là

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

.\bar.ps1 

vì đường ống giải quyết vấn đề cho tất cả các tập lệnh con cháu.

+0

Lưu ý: foobar.ps1 có lẽ nên kiểm tra xem đầu ra có được chuyển hướng hay không trước khi gọi foo.ps1; nếu nó không được chuyển hướng, nó có lẽ không nên được dẫn đến nhiều hơn. –

+0

cảm ơn câu trả lời của bạn! tôi thực sự đã thử nghiệm với ý tưởng về một tập lệnh mẹ-wrapper phổ biến hiện nay. thay vì nhiều hơn, tôi tìm thấy thành công với một cái gì đó như '$ output = invoke-command" $ args 2> & 1 "(mở rộng từ những gì tôi đã trình bày ở trên). Ngoài ra, tôi đánh giá cao bạn hãy xem tất cả 3 câu hỏi mở :) –