I am working on a function that will invoke plink.exe
to run commands on a remote non-Windows device. (In fact, this will be a more generalized function, but this is the use-case I am currently dealing with). In case the command does not work as expected, I am capturing the output of the EXE as an object that I can manipulate in my function and/or parent script.
The problem I am having is that when I capture the output, it seems to be incomplete.
Somehow I am not getting all the StandardOutput that the process generates. I can be sure of this since I am testing with plink.exe -v
which simply outputs the command usage help and is predictable each time.
Here is some sample code, cut down from what I am working on, but which exhibits this behaviour when I run in PowerShell ISE 5.0.
function Set-ProcessStartInfo ($FileName,$Arguments) {
$ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo
$ProcessStartInfo.FileName = $FileName
$ProcessStartInfo.Arguments = $Arguments
$ProcessStartInfo.UseShellExecute = $false
$ProcessStartInfo.WindowStyle = "Hidden"
$ProcessStartInfo.RedirectStandardError = $true
$ProcessStartInfo.RedirectStandardOutput = $true
return $ProcessStartInfo
}
function Write-StdOut ($LastEvent = 0){
foreach ($EventId in ($StdOut.Keys | Sort | ?{$_ -gt $LastEvent})) {
Write-Host "$EventId`t$($StdOut.$EventId)"
$LastEvent = $EventId
}
return $LastEvent
}
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo = (Set-ProcessStartInfo "plink.exe" "-v")
$Global:StdOut = @{}
$StdOutLastEvent = 0
$StdOutAction = {
if(-not $StdOut.ContainsKey($Event.EventIdentifier)) {
$StdOut.Add([System.Int64]$Event.EventIdentifier, [System.String]$EventArgs.Data)
}
}
$StdOutJob = Register-ObjectEvent -InputObject $Process -EventName OutputDataReceived -Action $StdOutAction
$Process.Start() | Out-Null
$Process.BeginOutputReadLine() | Out-Null
Clear-Host;Write-host;Write-Host
$Count = 0
do{
$Count++
Write-Verbose "Loop $Count starting"
Sleep -Milliseconds 250
$HasExisted = $Process.HasExited
Receive-Job $StdOutJob.Id
$StdOutLastEvent = Write-StdOut -LastEvent $StdOutLastEvent
Write-Verbose "Loop $Count complete"
} while (-not $HasExisted)
# $StdOutLastEvent = Write-StdOut -LastEvent $StdOutLastEvent
Stop-Job $StdOutJob.Id
Remove-Job $StdOutJob.Id
This gives the following:
VERBOSE: Loop 1 starting
1370 PuTTY Link: command-line connection utility
1371 Development snapshot 2008-12-04:8371
1372 Usage: plink [options] [user@]host [command]
1373 ("host" can also be a PuTTY saved session name)
1374 Options:
1375 -V print version information and exit
1376 -pgpfp print PGP key fingerprints and exit
VERBOSE: Loop 1 complete
There is a load of output missing (approx. 30 lines of text)
Workaround
If I uncomment the # $StdOutLastEvent = Write-StdOut -LastEvent $StdOutLastEvent
line at the end, I do get the remaining ~30 lines of text. I assume this has something to do with the asynchronous nature of BeginOutputReadLine()
, and the time it takes for the Job created by Register-ObjectEvent
to complete.
At the moment I am treating this as a workaround, since I have no way of knowing that I have collected everything.
Is there a way that I can be sure that I have received everything?