Piping in a file on the command-line using System.Diagnostics.Process
Both Mark and Luke gave me the right direction to go. I couldn't use either answer because I had to do this so that it could run with Mono in Linux. So I ended up writing to the StandardInput as suggested. Here is the code that works:
public static bool ExecuteSvnCommandWithFileInput( string command, string arguments, string filePath, out string result, out string errors )
{
bool retval = false;
string output = string.Empty;
string errorLines = string.Empty;
Process svnCommand = null;
var psi = new ProcessStartInfo( command );
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
try
{
Process.Start( psi );
psi.Arguments = arguments;
svnCommand = Process.Start( psi );
var file = new FileInfo(filePath);
StreamReader reader = file.OpenText();
string fileContents = reader.ReadToEnd();
reader.Close();
StreamWriter myWriter = svnCommand.StandardInput;
StreamReader myOutput = svnCommand.StandardOutput;
StreamReader myErrors = svnCommand.StandardError;
myWriter.AutoFlush = true;
myWriter.Write(fileContents);
myWriter.Close();
output = myOutput.ReadToEnd();
errorLines = myErrors.ReadToEnd();
// Check for errors
if ( errorLines.Trim().Length == 0 )
{
retval = true;
}
}
catch ( Exception ex )
{
string msg = ex.Message;
errorLines += Environment.NewLine + msg;
}
finally
{
if (svnCommand != null)
{
svnCommand.Close();
}
}
result = output;
errors = errorLines;
return retval;
}
System.Diagnostics.Process pipe (vertical bar) not accepted as argument
Thanks to Lee his comments, the problem has been resolved. Just invoke cmd.exe and pass it the full command:
var myProcess = new Process();
var p = new ProcessStartInfo("cmd.exe");
var sArgs = "/C ffmpeg.exe -i emp.mp3 -f wav - | neroAacEnc -ignorelength -q 0.5 -if - -of emp.mp4";
p.CreateNoWindow = false;
p.RedirectStandardOutput = false;
p.UseShellExecute = false;
p.Arguments = sArgs;
myProcess.StartInfo = p;
myProcess.Start();
myProcess.WaitForExit();
Piping output into to a windows executable started by [Diagnostics.Process]::Start?
Ansgar Wiechers' helpful answer contains an effective solution and sensible security warnings.
Using a System.Diagnostics.Process
instance with .RedirectStandardInput = $true
, and use of .StandardInput
to provide standard input after the process has started, gives you more flexibility, yet in your case the only modification that was needed was to pass your command line as an argument (2nd parameter), via option -c
, to program cmd.exe
(1st parameter).
[Diagnostics.Process]::Start()
's first parameter is only the executable name / path, not a full command line.It is the 2nd parameter that accepts a string containing the arguments to pass to the executable.
Since you're using shell features, namely connecting multiple commands with a pipeline, you must use
cmd.exe
as the executable, and pass your pipeline as an argument tocmd.exe
's/c
option.- You could use
powershell.exe
too, but in this simple case it is sufficient - and faster - to usecmd.exe
.
- You could use
Here's a simplified example:
$meProcessID = ([Diagnostics.Process]::Start(
# Program to launch
'cmd',
# Arguments to pass
'/c echo 42 | powershell -nop -c "''stdin input: '' + $Input" & pause'
).Id
The above demonstrates that stdin input 42
is seen by the powershell
process as such ($Input
); it opens a new console window that shows the following:
stdin input: 42
Press any key to continue . . .
How to get the output of a System.Diagnostics.Process?
What you need to do is capture the Standard Output stream:
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
string q = "";
while ( ! p.HasExited ) {
q += p.StandardOutput.ReadToEnd();
}
You may also need to do something similar with StandardError
. You can then do what you wish with q
.
It is a bit finicky, as I discovered in one of my questions
As Jon Skeet has pointed out, it is not smart performance-wise to use string concatenation like this; you should instead use a StringBuilder
:
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
StringBuilder q = new StringBuilder();
while ( ! p.HasExited ) {
q.Append(p.StandardOutput.ReadToEnd());
}
string r = q.ToString();
How To: Execute command line in C#, get STD OUT results
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "YOURBATCHFILE.bat";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Code is from MSDN.
Redirect a process's output to both a file and the console
Could use something like that
using System;
using System.Diagnostics;
namespace InteractWithConsoleApp
{
class Program
{
static void Main(string[] args)
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = @"C:\Windows\System32\cmd.exe";
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardInput = true;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = true;
Process cmdProcess = new Process();
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.ErrorDataReceived += cmd_Error;
cmdProcess.OutputDataReceived += cmd_DataReceived;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();
cmdProcess.StandardInput.WriteLine("ping google.com.ua"); //Execute ping google.com.ua
cmdProcess.StandardInput.WriteLine("exit"); //Execute exit.
cmdProcess.WaitForExit();
}
static void cmd_DataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output from other process");
Console.WriteLine(e.Data);
}
static void cmd_Error(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Error from other process");
Console.WriteLine(e.Data);
}
}
}
Related Topics
Reverse Sorted Dictionary in .Net
Size of a Class (Object) in .Net
Get List<> Element Position in C# Using Linq
How to Pass Objects into an Attribute Constructor
Foo.Cmd Won't Output Lines in Process (On Website)
Formatting Literal Parameters of a C# Code Snippet
Using Sse in C# Is It Possible
Publish a Project with Local Database
Detect Change of Resolution C# Winforms
How to Get Full Host Name + Port Number in Application_Start of Global.Aspx
C# Open File, Path Starting with %Userprofile%
How to Implement One "Catch'Em All" Exception Handler with Resume
Maybe a C# Compiler Bug in Visual Studio 2015
Inconsistent Accessibility Error with the Following C# Code. Why