如何捕捉异步过程输出在PowerShell中? [英] How to capture process output asynchronously in powershell?

查看:119
本文介绍了如何捕捉异步过程输出在PowerShell中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从我在PowerShell脚本启动一个进程捕获输出和​​错误并异步显示它的控制台。我发现一些文件通过MSDN和<一个这样做href=\"http://blogs.technet.com/b/heyscriptingguy/archive/2011/06/16/use-asynchronous-event-handling-in-powershell.aspx\">other博客的。

I want to capture stdout and stderr from a process that I start in a Powershell script and display it asynchronously to the console. I've found some documentation on doing this through MSDN and other blogs.

创建和运行下面的例子之后,我似乎无法得到被异步显示的任何输出。所有输出的进程终止时才会显示。

After creating and running the example below, I can't seem to get any output to be displayed asynchronously. All of the output is only displayed when the process terminates.

$ps = new-object System.Diagnostics.Process
$ps.StartInfo.Filename = "cmd.exe"
$ps.StartInfo.UseShellExecute = $false
$ps.StartInfo.RedirectStandardOutput = $true
$ps.StartInfo.Arguments = "/c echo `"hi`" `& timeout 5"

$action = { Write-Host $EventArgs.Data  }
Register-ObjectEvent -InputObject $ps -EventName OutputDataReceived -Action $action | Out-Null

$ps.start() | Out-Null
$ps.BeginOutputReadLine()
$ps.WaitForExit()

在这个例子中,我期待看到程序执行结束之前的喜在命令行上的输出,因为OutputDataReceived事件应该已经触发。

In this example, I was expecting to see the output of "hi" on the commandline before the end of program execution because the OutputDataReceived event should have been triggered.

我试图用这个其他可执行文件 - java.exe的,git.exe等,他们都具有相同的效果,所以我留下来想,有一些简单的说我不理解或有错过。需要做异步读取标准输出什么?

I've tried this using other executables - java.exe, git.exe, etc. All of them have the same effect, so I'm left to think that there is something simple that I'm not understanding or have missed. What else needs to be done to read stdout asynchronously?

推荐答案

不幸的是,如果你想正确地做到这一点异步读取并不容易。如果调用WaitForExit()没有超时,你可以使用类似这样的功能,我写(基于C#code):

Unfortunately asynchronous reading is not that easy if you want to do it properly. If you call WaitForExit() without timeout you could use something like this function I wrote (based on C# code):

function Invoke-Executable {
    # Runs the specified executable and captures its exit code, stdout
    # and stderr.
    # Returns: custom object.
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]$sExeFile,
        [Parameter(Mandatory=$false)]
        [String[]]$cArgs,
        [Parameter(Mandatory=$false)]
        [String]$sVerb
    )

    # Setting process invocation parameters.
    $oPsi = New-Object -TypeName System.Diagnostics.ProcessStartInfo
    $oPsi.CreateNoWindow = $true
    $oPsi.UseShellExecute = $false
    $oPsi.RedirectStandardOutput = $true
    $oPsi.RedirectStandardError = $true
    $oPsi.FileName = $sExeFile
    if (! [String]::IsNullOrEmpty($cArgs)) {
        $oPsi.Arguments = $cArgs
    }
    if (! [String]::IsNullOrEmpty($sVerb)) {
        $oPsi.Verb = $sVerb
    }

    # Creating process object.
    $oProcess = New-Object -TypeName System.Diagnostics.Process
    $oProcess.StartInfo = $oPsi

    # Creating string builders to store stdout and stderr.
    $oStdOutBuilder = New-Object -TypeName System.Text.StringBuilder
    $oStdErrBuilder = New-Object -TypeName System.Text.StringBuilder

    # Adding event handers for stdout and stderr.
    $sScripBlock = {
        if (! [String]::IsNullOrEmpty($EventArgs.Data)) {
            $Event.MessageData.AppendLine($EventArgs.Data)
        }
    }
    $oStdOutEvent = Register-ObjectEvent -InputObject $oProcess `
        -Action $sScripBlock -EventName 'OutputDataReceived' `
        -MessageData $oStdOutBuilder
    $oStdErrEvent = Register-ObjectEvent -InputObject $oProcess `
        -Action $sScripBlock -EventName 'ErrorDataReceived' `
        -MessageData $oStdErrBuilder

    # Starting process.
    [Void]$oProcess.Start()
    $oProcess.BeginOutputReadLine()
    $oProcess.BeginErrorReadLine()
    [Void]$oProcess.WaitForExit()

    # Unregistering events to retrieve process output.
    Unregister-Event -SourceIdentifier $oStdOutEvent.Name
    Unregister-Event -SourceIdentifier $oStdErrEvent.Name

    $oResult = New-Object -TypeName PSObject -Property ([Ordered]@{
        "ExeFile"  = $sExeFile;
        "Args"     = $cArgs -join " ";
        "ExitCode" = $oProcess.ExitCode;
        "StdOut"   = $oStdOutBuilder.ToString().Trim();
        "StdErr"   = $oStdErrBuilder.ToString().Trim()
    })

    return $oResult
}

它捕获标准输出,标准错误和退出code。用法示例:

It captures stdout, stderr and exit code. Example usage:

$oResult = Invoke-Executable -sExeFileName 'ping.exe' -cArgs @('8.8.8.8', '-a')
$oResult | Format-List -Force 

有关详细信息和替代的实现(在C#)读<一个href=\"http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/\">this博客文章。

For more info and alternative implementations (in C#) read this blog post.

这篇关于如何捕捉异步过程输出在PowerShell中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆