How should I restart a dotnetcore C# console app?
I have tried suggestions found for C# console apps, but doesnt work for dotnetcore.
(This is not asp.net, which is where so many dotnetcore answers point)
OK, so im going to assume in this answer that it is ok with you if your program will start a new instance of your program and then close itself.
Here we go:
Since a dotnet console app can be started from the console, I think the best way to start a new instance of your console application would be thorugh using shell commands. To run shell commands from your program, add this helper class to your application: (If you are using windows instead of mac/linux, please see the end of this post)
using System;
using System.Diagnostics;
public static class ShellHelper
{
public static string Shell(this string cmd)
{
var escapedArgs = cmd.Replace("\"", "\\\"");
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = $"-c \"{escapedArgs}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return result;
}
}
Then since this is a extension method, just import it and then create a string with the command to restart your app and then use the Shell() method.
So if you are in development and you normally start your app by running dotnet run then make sure you are in the proper directory and then just use this line of code "dotnet run".Shell();
If you want to get the feedback from running the command then just assign the return value like this string result = "dotnet run".Shell();
Then once you have started the new process you just exit your current program by either returning on your main method etc.
Please Note: The above code is for mac/linux, If you are on windows, then the following two lines of the above code:
FileName = "/bin/bash",
Arguments = $"-c \"{escapedArgs}\"",
Should be replaced with:
FileName = "cmd.exe",
Arguments = $"/c \"{escapedArgs}\"",
Related
I'm having issues with a process started in C# not outputting to console despite it's output being redirected.
I'm running a console application inside a unityci docker container which allows me to start unity in batch mode and is supposed to output something to console.
If I use bash to start unity using unity-editor -projectPath myProject -executeMethod myMethod -logFile - I get all the output displayed in console as expected.
If I use C# to start a bash process using the same arguments, I get no output.
Here's the code I'm using to start a new process:
void StartProcess()
{
string argsString = "-projectPath myPath -executeMethod myMethod -logFile -";
ProcessStartInfo startInfo = new ProcessStartInfo(argsString)
{
FileName = "/bin/bash",
Arguments = $"-c unity-editor \"{argsString}\"",
RedirectStandardOutput = true,
RedirectStandardError = false,
UseShellExecute = false,
CreateNoWindow = true,
};
using Process proc = Process.Start(startInfo);
OutputDataReceived += OnOutputDataReceived;
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void OnOutputDataReceived(object obj, DataReceivedEventArgs args)
{
if(args.Data.StartsWith("[")
Console.WriteLine(args.Data);
}
The unity-editor command is part of the unityci docker container I'm using. It does the following:
#!/bin/bash
if [ -d /usr/bin/unity-editor.d ] ; then
for i in /usr/bin/unity-editor.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
fi
xvfb-run -ae /dev/stdout "$UNITY_PATH/Editor/Unity" -batchmode "$#"
So in the end I have a console application that runs an executable that starts another application, and the console output gets lost somewhere in between.
Can someone explain where the output is going and how to get it to display in console?
I figured it out. The problem was how I was starting the process.
Instead of starting a instance of bash to run a script like this:
ProcessStartInfo startInfo = new ProcessStartInfo("/bin/bash", $"-c unity-editor \"{argsString}\"");
I should have just started the script as a process directly:
ProcessStartInfo startInfo = new ProcessStartInfo("unity-editor", argsString);
I was under the impression that you can't start bash scripts as processes directly as that would throw a System.ComponentModel.Win32Exception: Exec format error exception, but having #!/bin/bash as the first line of the script allows them to be run without issue.
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
Mostly just as a curiosity, I wrote a little app to start up Terminator shell on Windows, using Ubuntu/WSL and Xming window server.
Doing things manually from the shell, I can run Firefox, gedit, Terminator, etc on Windows, it's pretty cool.
So I checked the location of bash.exe using where bash and it returned...
C:\Windows\System32\bash.exe
However when I tried to run this code...
using (var xminProc = new Process())
{
xminProc.StartInfo.FileName = #"C:\Program Files (x86)\Xming\Xming.exe";
xminProc.StartInfo.Arguments = ":0 -clipboard -multiwindow";
xminProc.StartInfo.CreateNoWindow = true;
xminProc.Start();
}
using (var bashProc = new Process())
{
bashProc.StartInfo.FileName = #"C:\Windows\System32\bash.exe";
bashProc.StartInfo.Arguments = "-c \"export DISPLAY=:0; terminator; \"";
bashProc.StartInfo.CreateNoWindow = true;
bashProc.Start();
}
I get the error...
System.ComponentModel.Win32Exception: 'The system cannot find the file specified'
And checking my entire system for bash.exe reveals it really be in another place altogether...
I'm not sure if this location is one that I can rely on, I'm worried it's ephemeral and can change during a Windows Store update, although I may be wrong about that.
Why does the command prompt show bash.exe to be in System32 but it's really in another location altogether?
Can I get C# to also use the System32 location?
As #Biswapriyo stated first set the platafrom to x64 on your solution:
Then you may run on your ubuntu machine from c# as:
Console.WriteLine("Enter command to execute on your Ubuntu GNU/Linux");
var commandToExecute = Console.ReadLine();
// if command is null use 'ifconfig' for demo purposes
if (string.IsNullOrWhiteSpace(commandToExecute))
{
commandToExecute = "ifconfig";
}
// Execute wsl command:
using (var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"cmd.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
CreateNoWindow = true,
}
})
{
proc.Start();
proc.StandardInput.WriteLine("wsl " + commandToExecute);
System.Threading.Thread.Sleep(500); // give some time for command to execute
proc.StandardInput.Flush();
proc.StandardInput.Close();
proc.WaitForExit(5000); // wait up to 5 seconds for command to execute
Console.WriteLine(proc.StandardOutput.ReadToEnd());
Console.ReadLine();
}
I followed this_link and I was able to run a dummy python file from my c# code like this...
public JsonResult FetchscrapyDataUrl(String website)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\ProgramData\Anaconda3\python.exe";
start.Arguments = #"C:\Users\PycharmProjects\scraping_web\scrape_info\main.py";
//this is path to .py file from scrapy project
start.CreateNoWindow = false; // We don't need new window
start.UseShellExecute = false; // Do not use OS shell
//start.RedirectStandardOutput = true;// Any output, generated by application will be redirected back
start.RedirectStandardError = true; // Any error in standard output will be redirected back (for example exceptions)
Console.WriteLine("Python Starting");
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd(); // Here are the exceptions from our Python script
string result = reader.ReadToEnd(); // Here is the result of StdOut(for example: print "test")
Console.Write(result);
}
}
}
Now I know that I can run scrapy spider from a single file main.py like this...
from scrapy import cmdline
cmdline.execute("scrapy crawl text".split())
When I run main.py file from cmd in windows it works fine but it does not work when I run it from C# code .Net framework. The error is ...
"Scrapy 1.4.0 - no active project\r\n\r\nUnknown command: crawl\r\n\r\nUse \"scrapy\" to see available commands\r\n"
Any Idea how to run this...Or am i missing some path setting in windows ??
Or should I run my spider from C# in some other way??
You need to set the WorkingDirectory property
start.WorkingDirectory = #"C:\Users\PycharmProjects\scraping_web\scrape_info\"
Or you need to cd to that directory to make it work
I want my c# application (which I execute on a raspberry pi) to run a bash script whenever it starts..
basically : the script is located in /etc/init.d and is named mnw. I want whenever my c# application starts, it should execute a part of the mnw script.
If it was written it in the terminal would look like :
cd /etc/init.d
./mnw stop
I want this to happen right at the start of public static void Main(), I've been trying
ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = "/dev/init.d/./mnw", Arguments = "stop", };
Process proc = new Process() { StartInfo = startInfo, };
proc.Start();
but it says that stop is a unexpected argument, any ideas?
I have never used ProcessStartInfo on Mono / Linux, but have you tried calling via bash?
ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = "/bin/bash", Arguments = "/dev/init.d/mnw stop", };
Process proc = new Process() { StartInfo = startInfo, };
proc.Start();
Also, there is no issues with the executable bit on mnw?
While using the Process class like in the accepted answer works, I'
d like to add that there is a library called CliWrap that makes CLI calls like this much easier:
fluent API to build the calls
async support
call with custom environment variables
set working directory
ability to pipe stdout/stderr into a string
retrieve exit code
etc.
using OPs question as example:
var result = await Cli
.Wrap("/dev/init.d/mnw")
.WithArguments("stop")
.ExecuteBufferedAsync();
// result contains:
// -- result.StandardOutput (string)
// -- result.StandardError (string)
// -- result.ExitCode (int)
// -- result.StartTime (DateTimeOffset)
// -- result.ExitTime (DateTimeOffset)
// -- result.RunTime (TimeSpan)
if (result.ExitCode != 0)
Console.WriteLine("Command failed")
Thank you #Tyrrrz for this library!