I'm trying to write a console application that starts wsl in a background process and streams different commands to it for some automation stuff.
Important: I cannot just do wsl -- mycommand and then exit as I need wsl to stay running in the background thus I chose to keep the process alive as long as my application runs.
To start the process I'm using Process.Start
var startInfo = new ProcessStartInfo
{
FileName = "wsl",
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
var process = Process.Start(startInfo);
To execute commands I just feed them into process.StandardInput followed by \n
process.StandardInput.Write("uname -a\n");
process.StandardInput.Flush();
After that I'm able to read the commands output using e.g. process.StandardOutput.ReadLine.
The problem with the code is that I'm not able to detect when the command finished i.e. there will be no more output to capture.
uname -a outputs a single line so that case is easy to handle but what if the command writes multiple lines to stdout?
Is there a way to solve this or is my approach to do this already wrong to start with?
(I need a synchronous way to get the output after executing the command as I need to evaluate it after execution)
Related
I was trying to start a exe with arguments by Process.Start.
My first try is using Process.Start("Path/of/the/exe", "arguments of exe").
Here's my code snippets:
Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
However the initialization of this exe is a bit slow, and the result is, I can only start the exe but the failed passing arguments. The following is the screenshot:
which is exactly the same result that starts without arguments.
By referencing this post C# - Making a Process.Start wait until the process has start-up, I changed my code as follows:
var process = Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
System.Threading.Thread.Sleep(100);
process.Refresh();
}
however these changes does not work.
I think my goal is to wait until exe completely started and then run it with arguments, but I dont know how to implement this.
=====================================================
New additions:
if I type in arguments call 'D:\Work\202205\20220525\tunnel-for-cmd.txt' in this started process, I will get my result:
SO I think the input arguments should be OK?
=======================================
new addition 2:
code for checking outputstream end
It appears this is a console application and you are typing in the console after it starts. This typing is not arguments: Arguments are provided only when starting a new process and never change.
What you are doing is providing something to the standard input of the program. Console programs have three streams the OS provides (one input and two output). You need to redirect these to detect when the program has started and to provide the proper input.
Something like this:
// Start with stdio redirected
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = #"your exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
var p = System.Diagnostics.Process.Start(psi);
// Read until the udec> prompt
while(true)
{
var line = p.StandardOutput.ReadLine();
if(line.StartsWith("udec>"))
break;
}
// Write the command
p.StandardInput.WriteLine(#"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
// Read the result
p.StandardOutput.ReadToEnd();
I’m suffering with a problem for a month, I want to run the cmd command "sfc / scannow", before that I tried to read the result of this command via StandartOutput, but it’s not working with this command, I had an idea to press the button to call the console with this command and watch the result is in cmd, but I have a problem again, I just have cmd and everything is open, the command is not executed.I need to run the sfc / scannow command and see the process and the result of the check, in any way, but using C # (the project was created on win.forms)Please, help me
Also work with other cmd commands, I read through StandartOutput, but it doesn’t work with this command
string strCmdText;
strCmdText = "sfc/scannow";
Process cmdSFC = Process.Start(new ProcessStartInfo
{
UseShellExecute = true,
WorkingDirectory = #"C:\Windows\System32",
FileName = #"C:\Windows\System32\cmd.exe",
Arguments = "/c " + strCmdText,
});
cmdSFC.WaitForExit();
Set UseShellExecute to false. As described in MSDN:
Setting this property to false enables you to redirect input, output, and error streams.
I found some command line arguments to run generate the CSSLint report in xml format. It is working fine while running through command prompt.
Arguments:
csslint --format=csslint-xml "{SourceDir}\bootstrap.css" > "C:\temp\csslint.xml"
I want to execute it through C# application. I tried the below code.
Process process = new Process()
{
StartInfo =
{
FileName = "cmd.exe",
Arguments = "csslint --format=csslint-xml " + #"""{SourceDir}\bootstrap.css""" + #" > ""C:\Temp\CssLint.xml""",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
}
};
process.Start();
process.WaitForExit();
But it is not working. Can i have a solution or idea for this issue?
Also is there any way to generate the CSSLint report for the specified directory? I want to give the directory path instead of file name.
You need to add /Kor /C to cmd to execute a process passed as a parameter, thus:
Arguments = "/C csslint --format=csslint-xml " + #"""{SourceDir}\bootstrap.css""" + #" > ""C:\Temp\CssLint.xml""",
From the documentation:
Options
/C Run Command and then terminate
/K Run Command and then return to the CMD prompt.
This is useful for testing, to examine variables
One caveat... the piping (the > "C:\temp\csslint.xml" part of your command line) is not an argument, it's a redirection.
If you are redirecting your stdout (the RedirectStandardOutput = true) from your app, you can capture it directly from C#, no need to pipe it to a file like you are trying to do: you'd need to handle the Process.OutputDataReceived event between your Start and WaitForExit calls, or read from the Process.StandardOutput stream).
As for your second question, the csslint CLI allows passing in a directory instead of a file
I'm trying to launch ffmpeg as a Process in .NET (C#), but in some cases, depending on the arguments (specifically, if I omit video arguments in order to create an audio file), it's stalling. It launches, outputs some lines, but then just stalls (using 0% CPU). When the parent .NET process is killed, it continues, and if I let it continue, ffmpeg produces the file correctly. I thought it might be due to using Peek() to look at the stream, so I just simplified it to the following, which behaves the same:
_process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = true,
FileName = "c:\\ffmpeg.exe",
Arguments = string.Format(
"-i {0} {1} {2} {3} -y {4}", inputPath, videoArgs, audioArgs, options, outputPath)
}
};
_process.Start();
_process.WaitForExit();
ffmpeg gets to the point where it outputs information about the input video/audio streams before stalling. Executing the command via the command prompt works as expected.
Does anyone know what the problem could be?
Edit:
Just to add, I tried UseShellExecute = true (and RedirectStandardError = false), and this works. I still need to read the output, however, so this doesn't really help me.
Have a read of this
MSDN on RedirectStandardError
Apparently this is a bit fiddly and can deadlock if the output or error stream buffers get filled up. Sit's there waiting for you to read what it's wrote...
According to the comments on this answer, in some cases FFmpeg manipulates the screen memory directly without using the standard output and error streams. One example of this is the [file] already exists. Overwrite? [y/N] prompt.
I'm guessing you've run the program outside if .NET to see what the output is? If it is a prompt like the one above then it might pay to avoid the trouble and attempt to provide an argument that will skip it eg. the -y command that forces FFmpeg to overwrite a file.
I have a console app and a win forms app that both need to call out to a remote server for some data, they make a call to the command line part of Putty, plink.exe, to run a remote command over SSH.
I created a tiny class library for both to share, running the following:
public static string RunCommand(string command, string arguments) {
ProcessStartInfo startInfo = new ProcessStartInfo {
FileName = command,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true
};
string output = null;
using (Process p = new Process()) {
p.StartInfo = processStartInfo;
p.Start();
output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
return output;
}
Under the console application everything works fine, under the win forms it doesn't error, it seems that WaitForExit() just doesn't wait. I get an empty string for output. I've confirmed from the remote server the user logged in, so it seems the command has run.
Any ideas?
Under Windows Console applications have STDIN, STDOUT, and STDERR. Windowed applications do not. When you create a process under a Console application the STDIN etc. are inherited by the child application. This does not happen in the Windowed application.
The RedirectStandardInput=true works because it makes the system create a Writer for the STDIN that you can use to send input to the child process. In your case the child doesn't need the input it just needs the presence of the input. YMMV.