2017-03-01 104 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)'。 –