从事件中通过管道返回数据

时间:2013-10-11 14:01:15

标签: powershell powershell-v2.0

我目前正在编写一个脚本,该脚本向SVN.EXE实用程序发出命令以从Subversion返回信息。我以前使用过以下代码:

Function ShellSvnSynchronously {
    Param(
        [String] $command,
        [String] $arguments
    )

    $startInfo = New-Object System.Diagnostics.ProcessStartInfo
    $startInfo.Arguments = "$command $arguments"
    $startInfo.RedirectStandardError = $true
    $startInfo.RedirectStandardOutput = $true
    $startInfo.CreateNoWindow = $true
    $startInfo.FileName = "svn.exe"
    $startInfo.UseShellExecute = $false

    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $startInfo
    $process.Start() | out-null
    $process.WaitForExit()

    Write-Host "  ExitCode=" $process.ExitCode

    If ($process.ExitCode -eq 0) {
        $stdOut = $process.StandardOutput
        For(;;) {
            $line = $stdOut.ReadLine()
            if ($line -eq $null) { Break }
            # Return the line
            $line
        }
    } Else {
        $stdErr = $process.StandardError

        # Note. Force this to be an array.
        ThrowSvnError @( & {
            For(;;) {
                $line = $stdErr.ReadLine()
                If ($line -eq $null) {
                    Break
                } else {
                    ParseSvnErrorLine $line
                }
            }
        } )
    }   
}

这很有效 - 只要返回的输出不是太大。不幸的是,如果你返回了大量数据,SVN.EXE将无限期挂起。阅读这个问题(虽然不可否认它在C#中使用),似乎System.Diagnostics.Process中存在一个错误,当你重定向StdOut和StdErr导致死锁时。建议是使用异步代码。我已经重写了这个函数以使用异步方法,但是我已经意识到在文档和Web中找到的所有示例中,没有办法在原始函数的上下文中将数据发送到管道。以下是我到目前为止的尝试:

Function ShellSvnAsynchronously {
    Param(
        [String] $command,
        [String] $arguments
    )

    $startInfo = New-Object System.Diagnostics.ProcessStartInfo
    $startInfo.Arguments = "$command $arguments"
    $startInfo.RedirectStandardError = $true
    $startInfo.RedirectStandardOutput = $true
    $startInfo.CreateNoWindow = $true
    $startInfo.FileName = "svn.exe"
    $startInfo.UseShellExecute = $false

    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $startInfo

    Write-Host "Begin" -foregroundcolor DarkGreen

    # Set up events.
    Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -SourceIdentifier processErrorDataReceived -Action {
        $data = $EventArgs.data
        if (-not [string]::IsNullOrEmpty($data))
        {
            #Write-Host "Logging ERROR: $data" -ForegroundColor Red
            ParseSvnErrorLine $data
        }
    }
    Write-Host "Setup Register-ObjectEvent to Log Errors" -foregroundcolor DarkGreen

    # Register an Action for Standard Output Data Received Event
    Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -SourceIdentifier processOutputDataReceived -Action {
        $data = $EventArgs.data
        if (-not [string]::IsNullOrEmpty($data))
        {
            #Write-Host "Logging: $data" -ForegroundColor Blue
            #Write-Host "$data"
            #Write-Output $data
            $data
        }
    }
    Write-Host "Setup Register-ObjectEvent to Log StdOut" -foregroundcolor DarkGreen

    Write-Host "Starting Process..." -foregroundcolor DarkGreen

    $process.Start()
    $process.BeginOutputReadLine()
    $process.BeginErrorReadLine()
    $process.WaitForExit()

    Write-Host "Stopped Process." -foregroundcolor DarkGreen

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

是否有某种方法从调用函数获取管道,并将数据发送到其输入?

[后...]

我尝试了其他一些事情:

(1) 通过Register-ObjectEvent的-MessageData参数传递给集合,并将事件脚本块中的项添加到$ Event.MessageData。然后我有一个循环检查(-not $ process.HasExited),我在其中写回数据。可悲的是,由于缺乏同步,这引发了很多错误,我丢失了数据。令人讨厌的是,在Powershell 2.0中,我无法使用.NET 4.0 System.Collections.Concurrent命名空间中的集合,这本来不错。

(2) 删除了-Action脚本块,而是使用Wait-Event命令行开关等待事件。然而,无论-Timeout的值如何,这似乎立即返回。令人讨厌的是,这似乎是我最好的方式,尽管没有工作。不过,我希望能够选择多个-SourceIdentifier值,而不是按一个或不过滤!

0 个答案:

没有答案