Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Created December 6, 2017 23:39
Show Gist options
  • Save Jaykul/a6c935a536eae88aacc4b88049094b9c to your computer and use it in GitHub Desktop.
Save Jaykul/a6c935a536eae88aacc4b88049094b9c to your computer and use it in GitHub Desktop.
Like Start-Process, but without files
function Invoke-Process {
<#
.SYNOPSIS
Invokes a process, collecting the output
.DESCRIPTION
Invokes the process of the executable that is passed in using the command line arguments that are passed in
.EXAMPLE
Invoke-Process "C:\Program Files\7-Zip\7z.exe" 'x C:\temp -oC:\temp\path'
Invokes 7zip with the x (extract) option, a path, and the -o (output) path
.LINK
Start-Process
#>
[CmdletBinding()]
[OutputType([System.Diagnostics.Process], [String])]
param(
# Full path to the process exe
[Parameter(Mandatory, Position = 0)]
[ValidateNotNullOrEmpty()]
[ValidateScript( {Get-Command $_ -ErrorAction Stop; $True})]
[Alias("PSPath", "FilePath")]
[string]$ExePath,
# Run processes in the current window
[switch]$NoNewWindow,
# Output the process object INSTEAD OF its output
# If set, you are responsible to read the StandardInput and StandardError streams
[switch]$Passthru,
# Waits for the process to exit before continuing
[int32]$TimeOut = [int32]::MaxValue,
# The working directory to run the process in (defaults to PowerShell's current FileSystem location)
[string]$WorkingDirectory = $((Get-Location -PSProvider FileSystem).Path),
# Command Line Arguments
[Parameter(ValueFromRemainingArguments)]
[ValidateNotNullOrEmpty()]
[string[]]$Arguments
)
# Future Work: Add a way to pass strings for the StandardInput stream
# Add a way to specify exit codes that are acceptable
# Make the event handlers stream output to the console
$StartInfo = [System.Diagnostics.ProcessStartInfo]$ExePath
$StartInfo.CreateNoWindow = $NoNewWindow
$StartInfo.RedirectStandardError = $true
$StartInfo.RedirectStandardOutput = $true
$StartInfo.WorkingDirectory = $WorkingDirectory
$StartInfo.Arguments = $Arguments -join " "
$StartInfo.UseShellExecute = $false
$Process = [System.Diagnostics.Process]::new()
$Process.StartInfo = $StartInfo
$null = Register-ObjectEvent -InputObject $Process -EventName OutputDataReceived -Action {if ($EventArgs.Data) {[String[]]$Event.MessageData += $EventArgs.Data}} -MessageData $ProcessOutput
$null = Register-ObjectEvent -InputObject $Process -EventName ErrorDataReceived -Action {if ($EventArgs.Data) {[String[]]$Event.MessageData += $EventArgs.Data}} -MessageData $ProcessErrors
$Process.Start() | Out-Null
$Process.BeginOutputReadLine()
$Process.BeginErrorReadLine()
if (-not $Process.WaitForExit($TimeOut)) {
Write-Error "Process [$($ExePath)] has timed out after [$([TimeSpan]::FromMilliseconds($TimeOut))]"
}
if ($Passthru) {
$Process
if ($Process.ExitCode -ne 0) {
Write-Error "'$($ExePath) $($Arguments -join " ")' returned $($Process.ExitCode)."
}
return
}
# Normalize empty lines to only have one empty line together
$ProcessOutput -replace "[\r\n]{3,}", "`n`n" -split "\r?\n"
if ($ProcessErrors) {
# In errors write one error for each block of lines in a message
foreach ($message in $ProcessErrors -replace "[\r\n]{3,}", "`n`n" -split "\r?\n[\r\n]+") {
Write-Error "$($ExePath): $($message)"
}
}
if ($Process.ExitCode -ne 0) {
Write-Error "'$($ExePath) $($Arguments -join " ")' returned $($Process.ExitCode)."
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment