2016-08-06 3 views
2

Включение процесса PowerShell в лучшем случае шаткое, а ошибка - в случае необходимости что-то, что не спорадически виснет, как Start-Process, или захватывает вывод в конвейер, сохраняя при этом $ lastexitcode, большинство людей кажется для использования Process/ProcessStartInfo. Некоторые процессы много записывают на вывод или могут быть длинными, поэтому мы не хотим ждать, пока они не закончатся, чтобы увидеть выходной поток (не обязательно, чтобы хост ... мог быть файлом журнала). Так что я сделал эту функциюКомандная строка PowerShell Invoke

function Invoke-Cmd { 
<# 
.SYNOPSIS 
Executes a command using cmd /c, throws on errors and captures all output. Writes error and info output to pipeline (so uses .NET process API). 
#> 
    [CmdletBinding()] 
    param(
     [Parameter(Position=0,Mandatory=1)][string]$Cmd, 
     [Parameter(Position=1,Mandatory=0)][ScriptBlock]$ErrorMessage = ({"Error executing command: $Cmd - Exit Code $($p.ExitCode)"}), 
     [Parameter()][int[]]$ValidExitCodes = @(0) 
    ) 
    begin { 
     $p = New-Object System.Diagnostics.Process  
     $pi = New-Object System.Diagnostics.ProcessStartInfo 
     $pi.FileName = "cmd.exe" 
     $pi.Arguments = "/c $Cmd 2>&1" 
     $pi.RedirectStandardError = $true 
     $pi.RedirectStandardOutput = $true 
     $pi.UseShellExecute = $false 
     $pi.CreateNoWindow = $true   
     $p.StartInfo = $pi 

     $outputHandler = { 
      if ($EventArgs.Data -ne $null) { Write-Output $EventArgs.Data } 
     } 

     Write-Output "Executing..." 

     $stdOutEvent = Register-ObjectEvent -InputObject $p ` 
      -Action $outputHandler -EventName 'OutputDataReceived' 
     $stdErrEvent = Register-ObjectEvent -InputObject $p ` 
      -Action $outputHandler -EventName 'ErrorDataReceived' 
    } 
    process { 
     $p.Start() | Out-Null 

     $p.BeginOutputReadLine() 
     $p.BeginErrorReadLine() 

     $p.WaitForExit() | Out-Null 

    } 
    end {  
     Unregister-Event -SourceIdentifier $stdOutEvent.Name 
     Unregister-Event -SourceIdentifier $stdErrEvent.Name 

     if (!($ValidExitCodes -contains $p.ExitCode)) { 
      throw (& $ErrorMessage) 
     } 
    } 
} 

Проблема заключается в том, что Write-Output в моем обработчик событий не работает в том же контексте исполнения, как и сам Invoke-Cmd ... Как я могу получить свой обработчик событий к отложенной записи Вывод в выходной поток родительских функций?

Спасибо

ответ

1

Это должно делать то, что вы хотите:

cmd /c ... '2>&1' [ | ... ] 

Для захвата выходных данных в переменной:

$captured = cmd /c ... '2>&1' [ | ... ] 

Эта идиома:

  • выполняет внешняя команда переданасинхронно,
  • проходит как стандартный вывод и поток ошибок по трубопроводу, а выход становится доступным,
  • , если указано, захватывает комбинированный выход в переменной $captured,
  • сохраняет код завершения команды внешней в $LASTEXITCODE.

$LASTEXITCODE является automatic variable что устанавливается автоматически всякий раз, когда внешняя программа завершения работы, и устанавливается код выхода этой программы. Это глобальная одноэлементная переменная вы можете получить доступ без спецификатора области видимости из любой области (как если бы было объявлено с -Scope Global -Option AllScope, хотя Get-Variable LASTEXITCODE | Format-List не отражает это). Это означает, что только последний код завершения отражен в $LASTEXITCODE, независимо от того, в какой области он был запущен.
Обратите внимание, однако, что переменная не создается до тех пор, пока первая внешняя команда, запущенная в сеансе, не завершится Бег.

Caveat: Поскольку cmd, что является обработка 2&1 переназначение (из-за цитирование вокруг него), стандартный вывод и стандартный поток ошибок объединяются в источнике, так вы не сможете сказать, какие выходные линии поступали из потока; если вам нужно знать, используйте Переустановка PowerShell2&1 (пропустите цитирование).
Для обсуждения того, как эти два подхода отличаются, см. Мой this answer.

Если вы хотите вывести на консоль в дополнение к завоеванию в переменной (предполагается, что последний отрезок трубопровода называет Командлет:

cmd /c ... '2>&1' [ | ... -OutVariable captured ] 

Пример:

> cmd /c ver '&&' dir nosuch '2>&1' > out.txt 
> $LASTEXITCODE 
1 
> Get-Content out.txt 

Microsoft Windows [Version 10.0.10586] 
Volume in drive C has no label. 
Volume Serial Number is 7851-9F0B 

Directory of C:\ 

File Not Found 

Пример показывает, что код выхода правильно отражен в $LASTEXITCODE, а t как stdout, так и stderr были отправлены в выходной поток и захвачены PowerShell в файле out.txt.

Смежные вопросы