I would like to run a bash script using WSL from a WPF application. I have been using the Process.Start() method to try and run this script, passing some arguments along with it. I thought I had this function running perfectly before, however I am struggling to get the script to run on WSL.
Here is the code I have below, I can include the WSL script if needed.
I need to run the WSL script and pass some arguments to the script from the command line.
string path = "\"" + store.LPFolderPath + "\"";
Debug.WriteLine(path);
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
p.StartInfo = info;
p.Start();
if(p.StandardInput.BaseStream.CanWrite)
{
p.StandardInput.WriteLine(#"wsl");
p.StandardInput.WriteLine(#"cd path/To/Script");
//"./lpass == scriptFile "//
p.StandardInput.WriteLine(#"./lpass " + `ARG`);
p.StandardInput.WriteLine("exit");
p.StandardInput.Close();
};
p.WaitForExit();
From what I know this script does not run at all. Is there a mistake I am making utilizing Process or how would I go about debugging this C# call in order to find out my error?
This reminds me a bit of two questions I answered around using Python to launch WSL applications:
This one was using a subprocess.
And this one was using a SendKeys method.
In your case, you are attempting to use standard input, but I think the issue is similar. The first WriteLine launches WSL, but I believe it's now a separate process with its own standard input. The additional lines of standard input are probably going to the original process p, but are probably just waiting there until the WSL process terminates, which doesn't happen until your parent process terminates. Disclaimer - I haven't tried this myself; I'm just basing it on past experience with other languages.
The solution should be the same as I mention in the other answers -- Use arguments to the wsl command itself to call your script. E.g.:
p.StandardInput.WriteLine(#"wsl --cd path/To/Script -e bash -c lpass");
Or something similar. Try it from the command-line first:
wsl --cd path/To/Script -e bash -c lpass
The -e tells it to run the Bash shell, with the -c passing the ./lpass directly to Bash. The --cd is also a WSL argument which will set the initial starting directory, although it should be possible to move it into the Bash command as well:
wsl -e bash -c "cd path/To/Script; ./lpass"
If the lpass script has a shebang line, you can even bypass calling Bash:
wsl --cd path/to/Script -e ./lpass
Related
I have a very simple question:
How to start / stop a systemd service from a .NET 6.0 Console App?
Just to clarify: I do not want the service to stop itself. I want a console app to stop another service already installed on a Ubuntu Server 20.04.
More concrete: How would I call this line in C# properly?
sudo systemctl start SERVICE_NAME
Do I have to start a Process like this?
var process = new Process();
process.StartInfo.FileName = " /usr/lib/systemd"
process.StartInfo.Arguments = "start SERVICENAME";
process.Start();
What have I tried so far?
I googled but could not find any viable solution, and since I am no expert on Linux I might have fallen into the XY trap
I've never done this before. I like .NET and I use netcore every day but, if you want to create a script to be run only on linux. Maybe a bash script would do.
Since you want to use .NET, I think you'll need to do something like:
ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = "/bin/bash", Arguments = "systemctl stop SERVICE", };
Process proc = new Process() { StartInfo = startInfo, };
proc.Start();
You're creating a bash instance from where you call systemctl.
You also might want to wait until the process has finished. You can do that with
proc.WaitForExit();
Be aware this is a synchronous waiting.
I have a .NET 5 program on CentOS that starts from a systemd service by using a .service file. It needs to execute another program through the command line, so I wrote these codes:
var processStartInfo = new ProcessStartInfo()
{
FileName = "/bin/bash",
Arguments = "-c \"echo $PATH\"",
WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};
var process = Process.Start(processStartInfo);
process.WaitForExit();
When executing from CentOS's terminal, echo $PATH returns /usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/home/test_user/.local/bin:/home/test_user/bin.
But running echo $PATH using the above code give me /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Why are the 2 $PATH values different?
Searching about $PATH gave me a few results about /etc/profile, ~/.bash_profile, /etc/bash.bashrc and ~/.bashrc, but I'm not sure how to interprete these files.
The problem is that the above code invoke a non-interactive shell instead of an interactive shell like when using the system console. From GNU's Bash Reference Manual, section 1.2:
Shells may be used interactively or non-interactively. In interactive
mode, they accept input typed from the keyboard. When executing
non-interactively, shells execute commands read from a file.
Interactive and non-interactive shells have different ways of checking for $PATH value upon starting, as noted in section 6.2. As an interactive shell Bash may read from etc/profile, ~/.bash_profile, ~/.bash_login, and ~/.profile to check for $PATH values, but as a non-interactive shell Bash only accept whatever in the environment varible BASH_ENV to use as $PATH value:
Invoked non-interactively
When Bash is started non-interactively, to
run a shell script, for example, it looks for the variable BASH_ENV in
the environment, expands its value if it appears there, and uses the
expanded value as the name of a file to read and execute.
I have a problem with my code on linux. How do I keep app open after some bash commands are launched?
This code:
Process.Start("/bin/bash", " -c 'screen -S testScreen -d -m bash -c \"/home/test/Launcher.exe\"'");
Console.ReadKey();
What it does? it runs "Launcher.exe" in "testScreen", but for some reason, it closes program where that bit of code is there. I have there Console.ReadKey() in purpose and don't want to close it. Can someone explain me why does it close?
The Proces.Start method returns an object of type Process. After starting the bash command you can invoke WaitForExit on it.
var proc = Process.Start(...);
proc.WaitForExit();
Console.ReadKey();
So here's my problem:
Python scripts launched from C# via the Process class require the -i switch to be passed to python.exe or else they don't send any output when I redirect the StandardXxx streams
I want to bundle my Python program with py2exe (or another similar setup, if one meets my needs)
py2exe does not seem to let me pass the -i switch in any obvious way, but it's giving my the same issue as running python.exe - it doesn't output anything when launched by my C# program. So I need a way to force it into a similar mode so I can actually receive and send messages over stdin/stdout. I found some similar problems when it's built with "windows=['my_script']" but I built it with "console=['my_script']", so those fixes didn't help, and I don't need an actual interactive mode (i.e. the REPL), but for some reason the -i switch fixes the console IO issues.
This is the code I'm using to launch it:
ProcessStartInfo psi = new ProcessStartInfo();
psi.UseShellExecute = false;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.FileName = "py2exe_program.exe";
Process p = Process.Start(psi);
// program hangs here because ready message is never printed
p.StandardOutput.ReadLine(); // Consume ready message
The executable works as expected when launched externally, and the above code works when I launch the Python script via "python.exe -i my_script.py" but it runs into the same problem without the -i switch.
How do I get it to behave as expected?
I wrote a BAT script that automatically connects and disconnects a broad band connection:
netsh mbn connect interface="Mobile Broadband Connection" connmode=name name="My Provider"
netsh mbn disconnect interface="Mobile Broadband Connection"
When I click the BAT script it is working fine, but when I execute it with Process.Start:
var startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c reconnect.bat",
WindowStyle = ProcessWindowStyle.Minimized,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var process = new Process
{
StartInfo = startInfo
};
process.Start();
netsh returns an error that the mbn command was not found.
Before I was using a BAT file I started the commands directly. They worked fine on the shell, but got the same error when using Process.Start.
Why is this happening to me?
Output:
C:\Dev\NetworkAdapterTest\NetworkAdapterTest\bin\Debug>netsh
mbn connect interface=\"Mobile
Breitbandverbindung\" connmode=name
name=\"A1 2\" The following command
was not found: mbn connect
interface="Mobile Breitbandverbindung"
connmode=name name="A1 2".
C:\Dev\NetworkAdapterTest\NetworkAdapterTest\bin\Debug>netsh
mbn disconnect interface=\"Mobile
Breitbandverbindung\" The following
command was not found: mbn disconnect
interface="Mobile Breitbandverbindung"
Notice how the quoting is really wired. I got the same issues when I started the commands directly.
When I compile the solution with Visual Studio 2008 everything is working as intended.
Question is no longer relevant.
The content of your arguments variable doesn't seem to make a lot of sense. If your program is located in "C:\Temp", it will be: "C:\Temp\/c reconnect.bat".
If the bat file is in the same folder as your application, you might want to use this code:
var arguments = string.Format("/c \"{0}\"",
Path.Combine(Application.StartupPath, "reconnect.bat"));
The extra quotes, in case your path has spaces in it.
Instead of using "cmd.exe", have you tried starting the batch file directly? It should work without having to go through cmd.exe.
The other thing I would check is that you're using the correct path. The easiest way is to have the Bat in the same directory as your executable, or refer to the full path in the filename.
Stack Overflow - how to execute a batch file from windows form