c# Run python script and read output lead to - c#

I want to run python script but my problem is that i get all the output at once at the end and not line by line (my script print the time 10 times and sleep for 1 second after each time).
This is my code:
static void Main(string[] args)
{
string cmd = #"C:\test.py";
string pythonPath = #"C:\python.exe";
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = pythonPath;//cmd is full path to python.exe
start.Arguments = cmd;//args is path to .py file and any cmd line args
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
while (!process.StandardOutput.EndOfStream)
{
string result = process.StandardOutput.ReadLine();
Console.Write(result);
}
}
}
}
UPDATE
string cmd = #"C:\test.py";
string pythonPath = #"C:\Python37\python.exe";
Process process = new Process();
process.StartInfo.FileName = "python";
process.StartInfo.Arguments = cmd;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
private static void OutputHandler(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
Python script
import time
from datetime import datetime
count = 0
while count < 3:
print(datetime.now())
time.sleep(1)
count += 1
print('DONE')

Related

Unable to open Visual Studio Command Prompt pragmatically using VS 2015

I want to run visual studios command programmatically.I have tried the above code but no help.All I am getting is a command prompt with my project`s directory open.
I have used Execute("VS140COMNTOOLS") as input.
private void Execute(string vsEnvVar) {
var vsInstallPath = Environment.GetEnvironmentVariable(vsEnvVar);
if (Directory.Exists(vsInstallPath)) {
var filePath = vsInstallPath + "vsvars32.bat";
if (File.Exists(filePath)) {
//start vs command process
Process proc = new Process();
var command = Environment.GetEnvironmentVariable("ComSpec");
command = #"" + command + #"";
//var batfile = #"E:\Test\vstest.bat";
var args = string.Format("/S/K \" \"{0}\" \"", filePath);
proc.StartInfo.FileName = command;
proc.StartInfo.Arguments = args;
//proc.StartInfo.RedirectStandardInput = true;
//proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.UseShellExecute = false;
proc.Start();
} else {
Console.WriteLine("File Does not exists " + filePath);
}
}
}
Try this:
private Process Execute(string vsEnvVar)
{
Process process = new Process();
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");//assume location is in path. Otherwise use ComSpec env variable
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = psi;
// attach output events
process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.StartInfo = psi;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.StandardInput.WriteLine(string.Format("call \"%{0}%vsvars32.bat\""), vsEnvVar);
process.StandardInput.Flush();
return process;
}
Now you can execute any commands by writing to process.StandardInput
process.StandardInput.WriteLine(#"msbuild c:\MySolution.sln /t:Clean");

Run command line command passing STDIN [duplicate]

This question already has answers here:
Capturing console output from a .NET application (C#)
(8 answers)
Closed 6 years ago.
I need to spawn a child process that is a console application, and capture its output.
I wrote up the following code for a method:
string retMessage = String.Empty;
ProcessStartInfo startInfo = new ProcessStartInfo();
Process p = new Process();
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = command;
startInfo.FileName = exec;
p.StartInfo = startInfo;
p.Start();
p.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
using (StreamReader output = p.StandardOutput)
{
retMessage = output.ReadToEnd();
}
}
);
p.WaitForExit();
return retMessage;
However, this does not return anything. I don't believe the OutputDataReceived event is being called back, or the WaitForExit() command may be blocking the thread so it will never callback.
Any advice?
EDIT: Looks like I was trying too hard with the callback. Doing:
return p.StandardOutput.ReadToEnd();
Appears to work fine.
Here's code that I've verified to work. I use it for spawning MSBuild and listening to its output:
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.Start();
process.BeginOutputReadLine();
I just tried this very thing and the following worked for me:
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "<insert command line arguments here>";
processStartInfo.FileName = "<insert tool path here>";
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
// use the output
string output = outputBuilder.ToString();
Here's some full and simple code to do this. This worked fine when I used it.
var processStartInfo = new ProcessStartInfo
{
FileName = #"C:\SomeProgram",
Arguments = "Arguments",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(processStartInfo);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Note that this only captures standard output; it doesn't capture standard error. If you want both, use this technique for each stream.
I needed to capture both stdout and stderr and have it timeout if the process didn't exit when expected. I came up with this:
Process process = new Process();
StringBuilder outputStringBuilder = new StringBuilder();
try
{
process.StartInfo.FileName = exeFileName;
process.StartInfo.WorkingDirectory = args.ExeDirectory;
process.StartInfo.Arguments = args;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.EnableRaisingEvents = false;
process.OutputDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.ErrorDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var processExited = process.WaitForExit(PROCESS_TIMEOUT);
if (processExited == false) // we timed out...
{
process.Kill();
throw new Exception("ERROR: Process took too long to finish");
}
else if (process.ExitCode != 0)
{
var output = outputStringBuilder.ToString();
var prefixMessage = "";
throw new Exception("Process exited with non-zero exit code of: " + process.ExitCode + Environment.NewLine +
"Output from process: " + outputStringBuilder.ToString());
}
}
finally
{
process.Close();
}
I am piping the stdout and stderr into the same string, but you could keep it separate if needed. It uses events, so it should handle them as they come (I believe). I have run this successfully, and will be volume testing it soon.
It looks like two of your lines are out of order. You start the process before setting up an event handler to capture the output. It's possible the process is just finishing before the event handler is added.
Switch the lines like so.
p.OutputDataReceived += ...
p.Start();
Redirecting the stream is asynchronous and will potentially continue after the process has terminated. It is mentioned by Umar to cancel after process termination process.CancelOutputRead(). However that has data loss potential.
This is working reliably for me:
process.WaitForExit(...);
...
while (process.StandardOutput.EndOfStream == false)
{
Thread.Sleep(100);
}
I didn't try this approach but I like the suggestion from Sly:
if (process.WaitForExit(timeout))
{
process.WaitForExit();
}
You need to call p.Start() to actually run the process after you set the StartInfo. As it is, your function is probably hanging on the WaitForExit() call because the process was never actually started.
The answer from Judah did not work for me (or is not complete) as the application was exiting after the first BeginOutputReadLine();
This works for me as a complete snippet, reading the constant output of a ping:
var process = new Process();
process.StartInfo.FileName = "ping";
process.StartInfo.Arguments = "google.com -t";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += (sender, a) => Console.WriteLine(a.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
Here's a method that I use to run a process and gets its output and errors :
public static string ShellExecute(this string path, string command, TextWriter writer, params string[] arguments)
{
using (var process = Process.Start(new ProcessStartInfo { WorkingDirectory = path, FileName = command, Arguments = string.Join(" ", arguments), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }))
{
using (process.StandardOutput)
{
writer.WriteLine(process.StandardOutput.ReadToEnd());
}
using (process.StandardError)
{
writer.WriteLine(process.StandardError.ReadToEnd());
}
}
return path;
}
For example :
#"E:\Temp\MyWorkingDirectory".ShellExecute(#"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe", Console.Out);

Git diff gets stuck when run as .NET Process

Running a Git diff gets stuck, till killed when running as a System.Diagnostics.Process.
Code:
class Program
{
static void Main(string[] args)
{
ProcessStartInfo pInfo = new ProcessStartInfo();
pInfo.FileName = "git.exe";
pInfo.Arguments = "diff --name-only --exit-code V2.4-Beta-01 HEAD";
pInfo.WorkingDirectory = #"C:\Git";
pInfo.UseShellExecute = false;
pInfo.CreateNoWindow = true;
pInfo.RedirectStandardError = true;
pInfo.RedirectStandardOutput = true;
Process p = new Process();
p.StartInfo = pInfo;
p.Start();
p.WaitForExit(10000);
if (!p.HasExited)
{
p.Kill();
Console.WriteLine("Killed!!!");
}
Console.WriteLine(p.StandardOutput.ReadToEnd());
Console.WriteLine(p.StandardError.ReadToEnd());
Console.ReadLine();
}
}
How to avoid this and make the program exists normally without expiring its timeout?
The problem is that someone has to consume the stdout buffer or it will get filled and the process gets stucked (see explanation here). The diff I was trying retrieved 983 lines, which was causing a buffer overflow.
The following is a solution to my problem:
class Program
{
static void Main(string[] args)
{
ProcessStartInfo pInfo = new ProcessStartInfo();
pInfo.FileName = "git.exe";
pInfo.Arguments = "diff --name-only --exit-code V2.4-Beta-01 HEAD";
pInfo.WorkingDirectory = #"C:\Git";
pInfo.UseShellExecute = false;
pInfo.CreateNoWindow = true;
pInfo.RedirectStandardError = true;
pInfo.RedirectStandardOutput = true;
string output = string.Empty;
Process p = new Process();
p.StartInfo = pInfo;
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
output += e.Data + Environment.NewLine;
}
});
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
p.Close();
Console.WriteLine(output);
Console.ReadLine();
}
}

c# process/cmd output parsing to variables

I'm making gui to rtmp-plugin that is commandline program. I need way to read output data from cmd program to three variables: downloaded, time and done.
In cmd output is exsample "3000 kb / 12 sec (12%)" without "". How I can get 3000 to downloaded variable without kb and 12 to time without sec and 12 to done without () and %. My code to run cmd process below.
int downloaded, time, done;
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "Data/yle-dl/yle-dl.exe",
Arguments = "-o pasila.flv http://areena.yle.fi/tv/1755554 --rtmpdump rtmpdump.exe ",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = false
}
};
proc.Start();
You can catch program output and parse values using regex. Note that you should also catch error output as it happens often, that normal output is handled as an error.
Process process = new Process();
process.StartInfo.FileName = "Data/yle-dl/yle-dl.exe";
process.StartInfo.Arguments = "-o pasila.flv http://areena.yle.fi/tv/1755554 --rtmpdump rtmpdump.exe ";
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += new DataReceivedEventHandler(ReadOutput);
process.ErrorDataReceived += new DataReceivedEventHandler(ReadOutput);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
private static void ReadOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
Match m = Regex.Match(e.Data, "(\\d+)[^0-9]*(\\d+)[^0-9]*(\\d+)[^0-9]");
if (m.Success)
{
textBox1.Text = m.Result("$1");
string time = m.Result("$2");
string percent = m.Result("$3");
}
}
}
If there is already some kind of output from the commandline you can catch it by using BeginOutputReadLine.
Otherwise you can add the assembly to your program references and access the (if exists) variable that the program uses to indicate your state.

calling a ruby script in c#

How do make a call to a ruby script and pass some parameters and once the script is finished return the control back to the c# code with the result?
void runScript()
{
using (Process p = new Process())
{
ProcessStartInfo info = new ProcessStartInfo("ruby C:\rubyscript.rb");
info.Arguments = "args"; // set args
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
string output = p.StandardOutput.ReadToEnd();
// process output
}
}
Just to fill smaller gaps I've implemented the same functionallity with ability to access OutputStream asynchronously.
public void RunScript(string script, string arguments, out string errorMessage)
{
errorMessage = string.empty;
using ( Process process = new Process() )
{
process.OutputDataReceived += process_OutputDataReceived;
ProcessStartInfo info = new ProcessStartInfo(script);
info.Arguments = String.Join(" ", arguments);
info.UseShellExecute = false;
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = info;
process.EnableRaisingEvents = true;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
errorMessage = process.StandardError.ReadToEnd();
}
}
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
using ( AutoResetEvent errorWaitHandle = new AutoResetEvent(false) )
{
if ( !string.IsNullOrEmpty(e.Data) )
{
// Write the output somewhere
}
}
}

Categories

Resources