我目前正在编写一个脚本,该脚本向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值,而不是按一个或不过滤!