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!
Related
I'd like to set up a Visual C++ toolchain to be used inside my C# application. For the toolchain it's recommended to call vcvarsall (or some subvariant). My problem is that the calling process - my application - will not get to keep the environment set up by vcvarsall. Can this somehow be achieved?
// First set up the toolchain with vcvarsall
var vcvarsallProc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = vcvarsallPath,
Arguments = "x86",
UseShellExecute = false,
}
};
vcvarsallProc.Start();
vcvarsallProc.WaitForExit();
// Invoke the linker for example
var linkerProc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "LINK.exe",
Arguments = " foo.obj /OUT:a.exe",
UseShellExecute = false,
}
};
linkerProc.Start();
linkerProc.WaitForExit();
// ERROR: 'LINK' is not recognized as an internal or external command
Turns out, this is pretty much impossible without a hassle.
The best one can do is chain multiple commands, which will run in the same environment. It can be done like so:
var linkerProc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C ({vcvarsallPath} {vcvarsallArgs}) && (LINK.exe {linkArgs})",
UseShellExecute = false,
}
};
Hiding this behind a function like InvokeWithEnvironment, and it's pretty painless to use.
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();
}
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}\"",
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 have a batch file setEnv.bat.
#echo off
set input=C:\Program Files\Java\jdk1.8.0_131
SET MY_VAR=%input%
I want to run this batch file from C# application and want to access the newly set value of MY_VAR from c# application.
C#:
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName= "D:\\Check\\SetJavaHome.bat";
proc.StartInfo.WorkingDirectory =
System.Environment.CurrentDirectory;
proc.Start();
string myVar = Environment.GetEnvironmentVariable("MY_VAR");
Can someone help me in getting this working as expected?
Thanks in advance.
Check out this answer with the sample code:
https://stackoverflow.com/a/51189308/9630273
Getting the Environment variables directly from another process is not possible, but a simple workaround can be like:
Create a dummy bat file (env.bat) that will execute the required bat and echo the environment variable.
Get the output of this env.bat inside the process execution of C#.
The reason why you want to do this is a bit vague but if your only option is to run that batchfile from a call to Process.Start then the following trickery will let you promote the environment vars from the batch file to your own process.
This is the batch file I use:
set test1=fu
set test2=bar
The followng code opens a standard Command Prompt and then uses the StandardInput to send commands to the command prompt and receive the results with OutputDataReceived event. I basically caputure the output of the SET command and the parse over its result. For each line that contains an environment var and value I call Environment.SetEnvironmentVaruable to set the environment in our own process.
var sb = new StringBuilder();
bool capture = false;
var proc = new Process();
// we run cms on our own
proc.StartInfo.FileName = "cmd";
// we want to capture and control output and input
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
// get all output from the commandline
proc.OutputDataReceived += (s, e) => { if (capture) sb.AppendLine(e.Data); };
// start
proc.Start();
proc.BeginOutputReadLine(); // will start raising the OutputDataReceived
proc.StandardInput.WriteLine(#"cd \tmp"); // where is the cmd file
System.Threading.Thread.Sleep(1000); // give it a second
proc.StandardInput.WriteLine(#"setenv.cmd"); // run the cmd file
System.Threading.Thread.Sleep(1000); // give it a second
capture = true; // what comes next is of our interest
proc.StandardInput.WriteLine(#"set"); // this will list all environmentvars for that process
System.Threading.Thread.Sleep(1000); // give it a second
proc.StandardInput.WriteLine(#"exit"); // done
proc.WaitForExit();
// parse our result, line by line
var sr = new StringReader(sb.ToString());
string line = sr.ReadLine();
while (line != null)
{
var firstEquals = line.IndexOf('=');
if (firstEquals > -1)
{
// until the first = will be the name
var envname = line.Substring(0, firstEquals);
// rest is the value
var envvalue = line.Substring(firstEquals+1);
// capture what is of interest
if (envname.StartsWith("test"))
{
Environment.SetEnvironmentVariable(envname, envvalue);
}
}
line = sr.ReadLine();
}
Console.WriteLine(Environment.GetEnvironmentVariable("test2")); // will print > bar
This will bring the environment variables that are set by a command file into your process.
Do note you can achieve the same by creating a command file that first calls your batchfile and then start your program:
rem set all environment vars
setenv.cmd
rem call our actual program
rem the environment vars are inherited from this process
ConsoleApplication.exe
The latter is easier and works out of the box, no brittle parsing code needed.