2012-01-23 10 views
7

Estoy buscando una solución a la excepción 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. que también funcionaría en scripts llamados dentro del script que contiene "the fix".excepción de controlador de archivo de redirección de powershell 2.0

A los efectos de esta pregunta, decir que tengo dos guiones:

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" 

Y SE foo.ps1 ejecute así:

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

La salida esperada debe ser:

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 

Sin embargo, debido al fallo conocido, la salida es en realidad:

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 

lo tanto, el comportamiento observado es que los cambios hechos por " la corrección "no está siendo heredada por el script 'hijo' (bar.ps1, en este caso). Cuando bar.ps1 intenta escribir, se cuelga con fuerza. Si no lo prevengo de alguna manera en foo.ps1, también se bloqueará. ¿Qué puedo hacer antes/en llamar al bar.ps1 para evitar que bar.ps1 se bloquee cuando intenta escribir?

Restricciones:

  • PowerShell 2.0
  • La secuencia de comandos se deben ejecutar como anteriormente
  • No puedo modificar bar.ps1 (y no deben bloquearse al escribir en stderr).

ACTUALIZACIÓN

continuación es una solución de medio aceptable. Digo la mitad porque solo evita que el script 'padre' se cuelgue. El script 'hijo' aún falla mucho cuando intenta escribir. En el lado positivo, puede ir tan lejos como reconocer que la barra falló.

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" 

Respuesta

1

Si hace esto en foo.ps1 que resuelve el problema:

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

tubería de la salida a través de más oculta el hecho de que en última instancia se va a un archivo de la instancia secundaria de Powershell, evitando el error.

De hecho, si se crea un guión foobar.ps1 abuelo, que sólo se ejecuta foo.ps1:

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

Entonces no es necesario "el punto de referencia" en absoluto, y foo.ps1 puede ser

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

.\bar.ps1 

porque la tubería resuelve el problema para todos los scripts descendientes.

+0

Nota: foobar.ps1 probablemente debería comprobar si la salida se redirige o no antes de llamar a foo.ps1; si no se redirige, probablemente no se debe canalizar a más. –

+0

gracias por su respuesta! De hecho, hoy experimenté con la idea de un script padre-envoltura común. en lugar de más, encontré el éxito con algo como '$ output = invoke-command" $ args 2> & 1 "(extendido de lo que mostré arriba). también, le agradezco que eche un vistazo a las 3 preguntas abiertas :) –

Cuestiones relacionadas