Skip to content

Instantly share code, notes, and snippets.

@jeremybeavon
Last active September 16, 2016 02:32
Show Gist options
  • Save jeremybeavon/0016e8f4aa4ba6ca937178e4caa0c308 to your computer and use it in GitHub Desktop.
Save jeremybeavon/0016e8f4aa4ba6ca937178e4caa0c308 to your computer and use it in GitHub Desktop.
Powershell cmdlet that prints outs output as it happens
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Management.Automation;
using System.Threading;
[Cmdlet(VerbsLifecycle.Invoke, "Process")]
public sealed class InvokeProcessCommand : Cmdlet
{
private bool isProcessFinishedOrTimedOut;
public InvokeProcessCommand()
{
TimeoutMinutes = 60;
}
[Parameter(Mandatory = true)]
public string CommandName { get; set; }
[Parameter(Mandatory = true)]
public string ArgumentList { get; set; }
[Parameter]
public int TimeoutMinutes { get; set; }
protected override void ProcessRecord()
{
WriteObject(StartProcessInfo());
}
private bool StartProcessInfo()
{
using (Process process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = CommandName,
Arguments = ArgumentList,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
})
{
Queue<string> messages = new Queue<string>();
using (AutoResetEvent waitHandle = new AutoResetEvent(false))
{
DataReceivedEventHandler dataHandler = (sender, args) =>
{
if (args.Data != null)
{
lock (messages)
{
messages.Enqueue(args.Data);
}
waitHandle.Set();
}
};
process.OutputDataReceived += dataHandler;
process.ErrorDataReceived += dataHandler;
if (!process.Start())
{
WriteVerbose("Failed to start process: " + CommandName);
return false;
}
try
{
process.BeginErrorReadLine();
process.BeginOutputReadLine();
WriteVerbose("Successfull started process" + CommandName);
WriteVerbose(string.Format("Process will time-out after {0} minutes.", TimeoutMinutes));
WriteVerbose("Starting at " + DateTime.Now);
bool hasExitCode = false;
new Thread(() =>
{
hasExitCode = process.WaitForExit(TimeoutMinutes * 60 * 1000);
isProcessFinishedOrTimedOut = true;
waitHandle.Set();
}).Start();
WaitForExit(messages, waitHandle);
if (hasExitCode)
{
WriteVerbose("Finished at " + DateTime.Now);
if (process.ExitCode == 0)
{
WriteVerbose("Process has completed successfully.");
}
else
{
WriteVerbose("Process failed. ExitCode = " + process.ExitCode);
return false;
}
}
else
{
WriteVerbose(string.Format("{0} has not completed after {1} minutes - please investigate!", CommandName, TimeoutMinutes));
process.Kill();
return false;
}
}
catch (Exception exception)
{
WriteVerbose("Exception: " + exception);
return false;
}
}
}
return true;
}
private void WaitForExit(Queue<string> messages, WaitHandle handle)
{
while (true)
{
WriteMessages(messages);
if (isProcessFinishedOrTimedOut)
{
WriteMessages(messages);
return;
}
handle.WaitOne(TimeSpan.FromMinutes(TimeoutMinutes));
}
}
private void WriteMessages(Queue<string> messages)
{
lock (messages)
{
while (messages.Count != 0)
{
WriteVerbose(messages.Dequeue());
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment