2017-03-01 83 views
1

在一個相當受限制的環境中,我基本上只允許使用Powershell + Plink,如果我想自動化一些任務。Powershell - 輸入stdin之後輸入stdout/stderr不再有效

我想創建一個函數:

  • 顯示輸出如果需要
  • 捕獲所有的輸出(stdout和stderr),使其後可用於進一步分析或記錄來安慰到達/文件/無論
  • 輸入密碼自動

不幸的是,我輸入密碼輸出捕獲停止線後。當我僅捕獲stdout時,它曾經工作。在捕捉到stderr之後,沒有更多的運氣。

代碼:

function BaseRun { 
    param ($command, $arguments, $output = "Console") 

    $procInfo = New-Object System.Diagnostics.ProcessStartInfo 
    $procInfo.RedirectStandardOutput = $true 
    $procInfo.RedirectStandardError = $true 
    $procInfo.RedirectStandardInput = $true 
    $procInfo.FileName = $command 
    $procInfo.Arguments = $arguments 
    $procInfo.UseShellExecute = $false 

    $process = New-Object System.Diagnostics.Process 
    $process.StartInfo = $procInfo 
    [void]$process.Start() 

    $outputStream = $process.StandardOutput 
    $errorStream = $process.StandardError 
    $inputStream = $process.StandardInput 

    $outputBuffer = New-Object System.Text.StringBuilder 

    Start-Sleep -m 2000 
    $inputStream.Write("${env:password}`n") 

    while (-not $process.HasExited) { 
     do { 
      $outputLine = $outputStream.ReadLine() 
      $errorLine = $errorStream.ReadLine() 
      [void]$outputBuffer.Append("$outputLine`n") 

      if (($output -eq "All") -or ($output -eq "Console")) { 
       Write-Host "$outputLine" 
       Write-Host "$errorLine" 
      } 
     } while (($outputLine -ne $null) -and ($errorLine -ne $null)) 
    } 

    return $outputBuffer.ToString() 
} 
+0

我沒有現成的解決方案,但是IIRC問題是由於您正在同步進行的。所以我認爲你需要把'$ inputStream.Write'移動到一個新的線程/ RunSpace /不管。或者通過'Register-ObjectEvent $ Process OutputDataReceived ......'輸出集合'' – wOxxOm

+0

該死的,我希望我可以避免整個異步shebang。 – oblio

回答

2

基於@ w0xx0m的和@馬丁Prikryl的幫助下,我成功地使這個工作液:

Register-ObjectEvent -InputObject $process ` 
    -EventName OutputDataReceived -SourceIdentifier processOutputDataReceived ` 
    -Action { 
    $data = $EventArgs.data 
    if($data -ne $null) { Write-Host $data } 
} | Out-Null 

Register-ObjectEvent -InputObject $process ` 
    -EventName ErrorDataReceived -SourceIdentifier processErrorDataReceived ` 
    -Action { 
    $data = $EventArgs.data 
    if($data -ne $null) { Write-Host $data } 
} | Out-Null 

[void]$process.Start() 
$process.BeginOutputReadLine() 
$process.BeginErrorReadLine() 

$inputStream = $process.StandardInput 

Start-Sleep -m 2000 
$inputStream.Write("${env:password}`n") 

$process.WaitForExit() 

Unregister-Event -SourceIdentifier processOutputDataReceived 
Unregister-Event -SourceIdentifier processErrorDataReceived 

$inputStream.Close()  

爲了簡便起見,我移除的過程中推出的部分(這是一樣的,從上面的那個)和數據處理(在這個例子中我只是寫 - 託管它)。

1

當您閱讀標準輸出和標準錯誤,而且不能使用ReadLine

  • 首先,如果只有標準錯誤輸出和標準輸出沒有,則$outputStream.ReadLine()從不返回,你永遠無法與$errorStream.ReadLine()

  • 一個更大的問題是,對於輸出而言,Windows中只有有限的緩衝區。因此,當生成完整的stdout行之前有很多stderr時,stderr緩衝區會填滿,並且應用程序(Plink)會在下次嘗試寫入stderr時停止,等待stderr緩衝區被佔用。它永遠不會做,因爲你一直在等待一個錯誤的標準輸出緩衝區。僵局。

你必須使用一個簡單的Read永不同步等待,當沒有可用的輸出。

+0

所以,基本上,我必須在任何地方開始使用Read()。我以前使用的代碼在輸出爲空時有一箇中斷。但是,如果我這樣做,循環將跳過閱讀stderr,我想。 – oblio

+0

是的。我認爲你不需要這兩個嵌套循環。只需保持外部'while(-not $ process.HasExited)'。 –