исключение дескриптора файла перенаправления powershell 2.0 - PullRequest
7 голосов
/ 24 января 2012

Я ищу решение для исключения 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

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

Таким образом, наблюдаемое поведение состоит в том, что изменения, внесенные "исправлением", не наследуются скриптом 'child' (bar.ps1, в данном случае).Когда bar.ps1 пытается писать, он сильно падает.Если я не буду защищаться от этого как-то в foo.ps1, он тоже сильно рухнет.Что я могу сделать до / при вызове bar.ps1, чтобы предотвратить сбой bar.ps1 при попытке записи?

Ограничения:

  • Powershell 2.0
  • скрипт должен быть запущен как указано выше
  • Я не могу изменить bar.ps1 (и он не должен падать при записи в stderr).

UPDATE

Ниже приведено полуоприемлемое решение.Я говорю «половина», потому что это только предотвращает сбой «родительского» сценария.«Дочерний» сценарий все еще терпит неудачу, когда пытается писать.С положительной стороны, это может пойти так далеко, как признание того, что бар не удалось.

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"

1 Ответ

1 голос
/ 13 февраля 2012

Если вы сделаете это в 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, минуяошибка.

На самом деле, если вы создаете скрипт для прародителя foobar.ps1, который просто запускает foo.ps1:

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

Тогда вам вообще не нужно исправление, иfoo.ps1 может быть просто

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

.\bar.ps1

, потому что конвейер решает проблему для всех дочерних сценариев.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...