This sort of question has been asked before in varying degrees, but I feel it has not been answered in a concise way and so I ask it again.
I want to run a script in Python. Let's say it's this:
if __name__ == '__main__':
with open(sys.argv[1], 'r') as f:
s = f.read()
print s
Which gets a file location, reads it, then prints its contents. Not so complicated.
Okay, so how do I run this in C#?
This is what I have now:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = cmd;
start.Arguments = args;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
When I pass the code.py location as cmd and the filename location as args it doesn't work. I was told I should pass python.exe as the cmd, and then code.py filename as the args.
I have been looking for a while now and can only find people suggesting to use IronPython or such. But there must be a way to call a Python script from C#.
Some clarification:
I need to run it from C#, I need to capture the output, and I can't use IronPython or anything else. Whatever hack you have will be fine.
P.S.: The actual Python code I'm running is much more complex than this, and it returns output which I need in C#, and the C# code will be constantly calling the Python code.
Pretend this is my code:
private void get_vals()
{
for (int i = 0; i < 100; i++)
{
run_cmd("code.py", i);
}
}
The reason it isn't working is because you have UseShellExecute = false.
If you don't use the shell, you will have to supply the complete path to the python executable as FileName, and build the Arguments string to supply both your script and the file you want to read.
Also note, that you can't RedirectStandardOutput unless UseShellExecute = false.
I'm not quite sure how the argument string should be formatted for python, but you will need something like this:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "my/full/path/to/python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using(Process process = Process.Start(start))
{
using(StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
If you're willing to use IronPython, you can execute scripts directly in C#:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
private static void doPython()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(#"test.py");
}
Get IronPython here.
Execute Python script from C
Create a C# project and write the following code.
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
run_cmd();
}
private void run_cmd()
{
string fileName = #"C:\sample_script.py";
Process p = new Process();
p.StartInfo = new ProcessStartInfo(#"C:\Python27\python.exe", fileName)
{
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
Console.ReadLine();
}
}
}
Python sample_script
print "Python C# Test"
You will see the 'Python C# Test' in the console of C#.
I ran into the same problem and Master Morality's answer didn't do it for me. The following, which is based on the previous answer, worked:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = cmd;//cmd is full path to python.exe
start.Arguments = args;//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)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
As an example, cmd would be #C:/Python26/python.exe and args would be C://Python26//test.py 100 if you wanted to execute test.py with cmd line argument 100. Note that the path the .py file does not have the # symbol.
Actually its pretty easy to make integration between Csharp (VS) and Python with IronPython. It's not that much complex... As Chris Dunaway already said in answer section I started to build this inegration for my own project. N its pretty simple.
Just follow these steps N you will get your results.
step 1 : Open VS and create new empty ConsoleApp project.
step 2 : Go to tools --> NuGet Package Manager --> Package Manager Console.
step 3 : After this open this link in your browser and copy the NuGet Command.
Link: https://www.nuget.org/packages/IronPython/2.7.9
step 4 : After opening the above link copy the PM>Install-Package IronPython -Version 2.7.9
command and paste it in NuGet Console in VS.
It will install the supportive packages.
step 5 : This is my code that I have used to run a .py file stored in my Python.exe
directory.
using IronPython.Hosting;//for DLHE
using Microsoft.Scripting.Hosting;//provides scripting abilities comparable to batch files
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
class Hi
{
private static void Main(string []args)
{
Process process = new Process(); //to make a process call
ScriptEngine engine = Python.CreateEngine(); //For Engine to initiate the script
engine.ExecuteFile(#"C:\Users\daulmalik\AppData\Local\Programs\Python\Python37\p1.py");//Path of my .py file that I would like to see running in console after running my .cs file from VS.//process.StandardInput.Flush();
process.StandardInput.Close();//to close
process.WaitForExit();//to hold the process i.e. cmd screen as output
}
}
step 6 : save and execute the code
Set WorkingDirectory or specify the full path of the python script in the Argument
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "C:\\Python27\\python.exe";
//start.WorkingDirectory = #"D:\script";
start.Arguments = string.Format("D:\\script\\test.py -a {0} -b {1} ", "some param", "some other param");
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
I am having problems with stdin/stout - when payload size exceeds several kilobytes it hangs. I need to call Python functions not only with some short arguments, but with a custom payload that could be big.
A while ago, I wrote a virtual actor library that allows to distribute task on different machines via Redis. To call Python code, I added functionality to listen for messages from Python, process them and return results back to .NET.
Here is a brief description of how it works.
It works on a single machine as well, but requires a Redis instance. Redis adds some reliability guarantees - payload is stored until a worked acknowledges completion. If a worked dies, the payload is returned to a job queue and then is reprocessed by another worker.
had same issure and this worked for me:
using IronPython.Hosting;
var engine = Python.CreateEngine();
engine.ExecuteFile("") //put the directory of the program in the quote marks
I am currently writing an app to start the screensaver on Windows 10 and show the screen instead of a black background. So that Bubbles and relatives can act like in older OS version.
Here is my full code:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
public class DrawOverMyScreen {
public static void Main(string[] CommandLine) {
switch (CommandLine[0]) {
case "/c":
DialogResult Answer = MessageBox.Show("What do you want to do?\n\n - Press \"Yes\" to configure the screensaver\n - Press \"No\" to change the screensaver\n - Press \"Cancel\" to do nothing", "DrawOverMyScreen Configuration", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);
switch (Answer) {
case DialogResult.Yes:
Screensaver("/c");
break;
case DialogResult.No:
throw new NotImplementedException();
break;
default:
break;
}
break;
default:
Screensaver("/s");
break;
}
}
public static void Screensaver(string CommandLine) {
RegistryKey Settings = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\DrawOverMyScreen");
if (Settings != null) {
string ScreensaverLocation = Settings.GetValue("Screensaver", string.Empty).ToString();
if (!string.IsNullOrEmpty(ScreensaverLocation) && File.Exists(ScreensaverLocation)) {
Process Screensaver = Process.Start(new ProcessStartInfo(ScreensaverLocation, CommandLine));
Screensaver.WaitForExit();
}
}
}
}
Notice the Screensaver method. It uses Process.Start(new ProcessStartInfo(ScreensaverLocation, CommandLine)); to start the screensaver. But whenever I do Screensaver("/c"); to run the screensaver's config utility, I only get the normal screensaver view (The one you get when idle after a certain time). Using the run prompt like this: C:\Windows\SysWOW64\SCREEN~1.SCR /c also gives the same result, but command line prompt actually opens the config utility.
Why won't it work, and how can I make it so it works?
Just from what you have provided, I can't tell you why it won't work. I do not have a screensaver to test that with (that I know of). But I'm able to do all four of these with Notepad opening a text file:
Separate ProcessStartInfo
ProcessStartInfo procInfo = new ProcessStartInfo("notepad.exe", "c:\\test.txt");
Process proc = Process.Start(procInfo);
proc.WaitForExit();
Separate ProcessStartInfo with Properties
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.Arguments = "c:\\test.txt";
procInfo.FileName = "notepad.exe";
Process proc = Process.Start(procInfo);
proc.WaitForExit();
Inline ProcessStartInfo
Process proc = Process.Start(new ProcessStartInfo("notepad.exe", "c:\\test.txt"));
proc.WaitForExit();
No PSI, Just Process
Process proc = Process.Start("notepad.exe", "c:\\test.txt");
proc.WaitForExit();
You may want to go with the first one so that you can breakpoint on the "Process proc..." line and examine the properties of procInfo. The Arguments property should show the 2nd value (in my case, c:\\test.txt), and the FileName property should be the path to what you are executing (mine is notepad.exe).
EDIT: I added the separate one with properties so you can really see explicit setting.
CONFIGURE A SCREENSAVER
I have worked out an example using the 3D Text screensaver:
string scrPath = #"C:\Windows\System32\ssText3d.scr";
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.FileName = scrPath;
procInfo.Verb = "config";
procInfo.UseShellExecute = false;
Process proc = Process.Start(procInfo);
proc.WaitForExit();
I didn't use the Arguments. Instead, I used the Verb. This requires UseShellExecute to be set to false. I got the expected configuration dialog instead of the screensaver running.
More About Verbs
This is where the verbs are for screen savers.
You can also define custom verbs: Register an Application to Handle Arbitrary File Types
I have a c# application currently running from the Terminal. It opens up a new terminal to execute a certain command then closes that terminal. I'm wondering if I can just execute this command in the terminal that is currently opened, instead of opening a new one. My code to execute the command is as follows.
Process proc = new System.Diagnostics.Process();
proc.StartInfo.WorkingDirectory = #"MyDirectory";
proc.StartInfo.FileName = #"/usr/bash";
proc.StartInfo.Arguments = command;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
How would I rewrite this without opening a new terminal?
As far as I know, no - but you can use the code
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
to make it so that the window does not pop up. Then in your normal terminal you can use print to update the user with a message like "Running Script..."
You can directly use libc system vs. the Diagnostics.Process and the redirection pipes (which just wrap the libc functions):
using System;
using System.Runtime.InteropServices;
namespace ShellMe
{
class MainClass
{
[DllImport ("libc")]
private static extern int system (string exec);
public static void Main (string[] args)
{
system("YourCommand2Run"); // blocking, you are in a shell so same rules apply
system("YourCommand2Run &"); // non-blocking
}
}
}
I need to write a windows form app, WPF, or console application that should run this from a command prompt:
#echo off
echo test
or
del c:\b.txt
The c# code needs to be on a button but It's not important now.
I tried this:
using System;
using System.Diagnostics;
using System.ComponentModel;
private void button1_Click(object sender, EventArgs e)
{
Process process = new Process();
process.StartInfo.FileName = "cmd.exe";
//process.StartInfo.WorkingDirectory = "c:\temp";
//process.StartInfo.Arguments = "somefile.txt";
Process.Start("cmd", "/dir");
}
but i can't put my CMD code in this ^ code.
I believe what you want is this:
Process p = new Process();
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
// add /C for command, a space, then command
psi.Arguments = "/C dir";
p.StartInfo = psi;
p.Start();
The above code allows you to execute the commands directly in the cmd.exe instance. For example, to launch command prompt and see a directory, you assign the Arguments property of the ProcessStartInfo instance to "/C dir", which means execute the command called dir.
Another approach you could use is seen here:
// put your DOS commands in a batch file
ProcessStartInfo startInfo = new ProcessStartInfo("action.bat");
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = "C:\\dir";
Process.Start(startInfo);
Put the above code in your click event.
Create a batch file with your commands using instructions found at http://www.ehow.com/how_6534808_create-batch-files.html
In my code example, if you name the batch file 'action.bat' and place it in th c:dir folder, everything will work the way you want.
Cheers :)
I found this thread very helpful and I would like to ask Ian Norton about his wrapper. HERE is the link to the wrapper I was trying that IanNorton had posted. I'm not sure if this is the right place to ask and I also don't want to create a new thread when it pertains to his response. So I will go ahead and suffer whatever backlash may come my way.
I am currently trying to use your wrapper and i cannot seem to seem to get it to trigger anything when I run it. I do not want to use options as i just want to set this as an .exe that runs on a timer. Quite simply, I would like to use the p4 opened -a cmd and print the out puts to a file. That is it. Any help would be greatly appreciated by this NooB.
Thank you very much!
This is as far as I got with just using the Command Line. Unfortunately I could not output my info to a text file.
using System;
using System.Diagnostics;
using System.IO;
namespace P4OpenCMD
{
class P4Opened
{
// Main begins program execution.
static void Main(string[] args)
{
string temp = string.Empty;
if (temp != string.Empty)
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
StreamWriter sw = p.StandardInput;
using (sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("set P4PORT=####");
sw.WriteLine("set P4USER=####");
sw.WriteLine("set P4CLIENT=####");
sw.WriteLine("set P4PASSWD=####");
sw.WriteLine("p4 opened -a //Depot/...");
sw.WriteLine("pause;");
}
Console.WriteLine();
}
sw.Close();
p.WaitForExit();
p.Close();
}
}
}
}
If you do this, you should be in good shape:
info.FileName = "cmd.exe";
info.Arguments = "/c p4 opened > opened.txt";