Private/Invoke-NativeCommandCapture.ps1
|
function Invoke-NativeCommandCapture { param( [Parameter(Mandatory)] [string]$FilePath, [string[]]$ArgumentList = @(), [string]$WorkingDirectory = (Get-Location).Path ) $tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString()) $stdoutPath = Join-Path $tempRoot 'stdout.txt' $stderrPath = Join-Path $tempRoot 'stderr.txt' $null = New-Item -ItemType Directory -Path $tempRoot -Force try { $psi = [System.Diagnostics.ProcessStartInfo]::new() $psi.FileName = $FilePath $psi.WorkingDirectory = $WorkingDirectory $psi.UseShellExecute = $false $psi.CreateNoWindow = $true $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true if ($psi.PSObject.Properties.Name -contains 'StandardOutputEncoding') { $psi.StandardOutputEncoding = [System.Text.UTF8Encoding]::new($false) } if ($psi.PSObject.Properties.Name -contains 'StandardErrorEncoding') { $psi.StandardErrorEncoding = [System.Text.UTF8Encoding]::new($false) } foreach ($arg in $ArgumentList) { [void]$psi.ArgumentList.Add([string]$arg) } $process = [System.Diagnostics.Process]::new() $process.StartInfo = $psi if (-not $process.Start()) { throw "Failed to start native process: $FilePath" } $stdoutTask = $process.StandardOutput.ReadToEndAsync() $stderrTask = $process.StandardError.ReadToEndAsync() $process.WaitForExit() [System.Threading.Tasks.Task]::WaitAll(@([System.Threading.Tasks.Task]$stdoutTask, [System.Threading.Tasks.Task]$stderrTask)) $stdoutText = $stdoutTask.Result $stderrText = $stderrTask.Result [pscustomobject]@{ ExitCode = $process.ExitCode StdOutLines = if ([string]::IsNullOrWhiteSpace($stdoutText)) { @() } else { @($stdoutText -split "`r?`n") | Where-Object { $_ -ne '' } } StdErrLines = if ([string]::IsNullOrWhiteSpace($stderrText)) { @() } else { @($stderrText -split "`r?`n") | Where-Object { $_ -ne '' } } } } finally { Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue } } |