C# Piping Linux Command Output in Process - c#

I am having trouble with the Process class to pipe a command on a Linux system.
I want to execute the following command: rpm2cpio repo.rpm | cpio -divm
I've tried
process.StartInfo.FileName = "rpm2cpio;
rocess.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.Arguments = "repo.rpm | cpio - idmv";
But the program hangs.
Similarly, I tried saving the output from rpm2cpio to a string or an output file and then pass that as the argument for the cpio command, but it also hangs.
process.StartInfo.FileName = "cpio";
rocess.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.Arguments = "-idvm < output.txt";
// or
process.StartInfo.Arguments = "-idvm < " + rp2cpio_output;
What are some ways I can get this working? I saw this post with a solution, but it is on a Window's system. How do the same thing on Linux?

Setting process.StartInfo.RedirectStandardOutput=true will cause the program to redirect standard output to the stream process.StartInfo.StandardOutput. When this happens the program will hang until you read from standard output.
To get the behavior I think you are looking for, you just need to set RedirectStandardOutput=false. That way the pipes and redirects in your command will work as expected.

Rather than directly writing to a file, you can simply use a StreamWriter to fetch the output in a stream buffer and then use that to write to the file. If the process still hangs, simply use the timeout command of linux to terminate the process.
The following snippet may help after making a few changes:
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.FileName = "/bin/bash";
processStartInfo.WorkingDirectory = "/";
string cmd = "timeout 1 cat > temp.txt";
var escapedArgs = cmd.Replace("\"", "\\\"");
processStartInfo.Arguments = $"-c \"{escapedArgs}\"";
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8;
processStartInfo.StandardInputEncoding = System.Text.Encoding.UTF8;
processStartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
processStartInfo.UseShellExecute = false;
process.StartInfo = processStartInfo;
process.Start();
stdIOWriter = process.StandardInput;
stdIOWriter.WriteLine("Hey Fellas");
String error = process.StandardError.ReadToEnd();
String output = process.StandardOutput.ReadToEnd(); ```

Related

How to grab the output percentage of ADB.exe push command process in c#?

I'm creating an app that copy files to android device.
When I run via cmd or powershell, it shows a percentage of copied files, but when using the following code, those not redirect the output.
I try to create a CMD process, an ADB process, powershell, nothing, only shows the output at the end of the precess.
folderToLoad = txtOBBFolder.Text;
process = new Process();
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + #"\adb.exe";
process.StartInfo.Arguments = #"push " + folderToLoad + " /sdcard/Android/obb/" + Path.GetFileName(folderToLoad);
process.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = false;
process.Start();
q = "";
while (!process.HasExited)
{
q += process.StandardOutput.ReadToEnd();
q += process.StandardError.ReadToEnd();
txtOutput.AppendText(q);
}
Any one as a better way to make this?
If the adb push takes a while, for the user, it seems that the program hang up, thats why I would like to show a progress bar for example, with the percentage that is being sent.

Run .exe from another app and enter values automatically

I want to run .exe file from another app, which is console app in .NET Core. When the app is open I want to write input to console from my code. Something like this:
var cmd = System.Diagnostics.Process.Start("myApp.exe");
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine("MyName"); // this should be entered in console as username
So I specify username from my code, instead of writing it to console manually. Code above is not working for me. Is there a way to do this?
enter username
You're first starting the Process and then you're manipulating the startup arguments. You need to start the process afterwards.
use something like this instead:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = false; //required to redirect standart input/output
// redirects on your choice
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.FileName = ...app path to execute...;
startInfo.Arguments = ...argumetns if required...;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.StandardInput.WriteLine(...write whatever you want...);
source
You need redirect standard input. Create ProcessStartInfo structure, then start a process and after process started write to the process standart input
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = pathToApplication,
RedirectStandardInput = true,
UseShellExecute = false
};
Process process = Process.Start(processStartInfo);
var writer = process.StandardInput;
writer.WriteLine("MyName");
Console.ReadLine();

Deadlock in System.Diagnostic.Process

I have a small console application and I want to read the output in C#. Therefore I've created this code snippet. The command prompt opens, but nothing is displayed.
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
process.StartInfo.FileName = DirectoryPath + "Test.exe";
process.StartInfo.Arguments = "-showAll";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.WaitForExit(2000);
String strOutput = process.StandardOutput.ReadToEnd();
If I remove UseShellExecute, RedirectStandardOutput and the last line, the command prompt opens and the Test.exe is shown, but I need the output as String and so I have to use these attributes to read the StandardOutput
I've also tried to set a timeout of 2 seconds (process.WaitForExit(2000)), but the empty command prompt does not close after 2 seconds.
If I close the empty command prompt manually in debug mode, the variable strOutput has my requested information.
To avoid deadlock you have to read the output stream before you wait to exit. So try :
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
process.StartInfo.FileName = DirectoryPath + "Test.exe";
process.StartInfo.Arguments = "-showAll";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
String strOutput = process.StandardOutput.ReadToEnd();
process.WaitForExit();

Write only the output from the console command

I'm trying to build a .net application that will run some console commands (like running phantomJs) and return me the outcome of the operations. But by default I'm getting everything from the starting of cmd.exe to closing it. Any ideas for a quick fix or do I need to play with regexes ?
Here's my code as for now :
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi);
System.IO.StreamReader sOut = proc.StandardOutput;
System.IO.StreamWriter sIn = proc.StandardInput;
sIn.WriteLine("phantomjs -v");
sIn.WriteLine("EXIT");
proc.Close();
string results = sOut.ReadToEnd().Trim();
sIn.Close();
sOut.Close();
PhantomJS is an executable (according to their docs) - why not execute that directly rather than running cmd.exe? That will avoid the cmd.exe noise.
Or redirect the output of phantomjs to a log file and load the log file.
Or if you absolutely have to use cmd.exe and can't redirect ... I'd maybe throw some echo sentinels around the phantomjs to serve as parse start/stop points.
e.g.,
echo PARSE START
runcommand.exe
echo PARSE STOP
But don't do that.
Instead of using the different streams. Why not use cmd as filename and pass it the -c "phantomjs -v" as argument. Then use proc.StandardOutput.ReadToEnd() to grab everything that is outputted in the console. This should leave out unneeded info as it only reads what the output of the executed command is.
Following code might not work, but should give you the general idea.
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "cmd";
psi.Arguments = "/c \"phantomjs -v\"";
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
// Optional other options
Process proc = Process.Start(psi);
string output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
If you are on an unix machine:
sIn.WriteLine("phantomjs -v > /dev/null");
Windows:
sIn.WriteLine("phantomjs -v > NUL");
I hope that the following would be helpful!
{
Process xyProcess = new Process();
xyProcess.StartInfo.FileName = "FilenameYouWant";
xyProcess.StartInfo.UseShellExecute = false;
xyProcess.StartInfo.CreateNoWindow = true;
xyProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
xyProcess.StartInfo.RedirectStandardInput = true;
xyProcess.StartInfo.RedirectStandardOutput = true;
xyProcess.StartInfo.RedirectStandardError = true;
xyProcess.StartInfo.Arguments += "any arg1 you want ";
xyProcess.StartInfo.Arguments += "any arg2 you want ";
xyProcess.EnableRaisingEvents = true;
xyProcess.OutputDataReceived += process_DataReceived;
// Start the process
xyProcess.Start();
xyProcess.BeginErrorReadLine();
xyProcess.BeginOutputReadLine();
xyProcess.WaitForExit();
}
static private void process_DataReceived(object sender, DataReceivedEventArgs e)
{
//Catch the process response here
}

Running an exe in C# with commandline parameters, suppressing the dos window

I am using lame for transcoding for one of my project. The issue is that when I call lame from C#, a DOS window pops out. Is there any way I can suppress this?
Here is my code so far:
Process converter =
Process.Start(lameExePath, "-V2 \"" + waveFile + "\" \"" + mp3File + "\"");
converter.WaitForExit();
Did you try something like:
using( var process = new Process() )
{
process.StartInfo.FileName = "...";
process.StartInfo.WorkingDirectory = "...";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.Start();
}
Assuming you are calling it via Process.Start, you can use the overload that takes ProcessStartInfo that has its CreateNoWindow property set to true and its UseShellExecute set to false.
The ProcessStartInfo object can also be accessed via the Process.StartInfo property and can be set there directly before starting the process (easier if you have a small number of properties to setup).
Process bhd = new Process();
bhd.StartInfo.FileName = "NSOMod.exe";
bhd.StartInfo.Arguments = "/mod NSOmod /d";
bhd.StartInfo.CreateNoWindow = true;
bhd.StartInfo.UseShellExecute = false;
Is another way.
This is my code that does a similar thing, (and also reads the output and return code)
process.StartInfo.FileName = toolFilePath;
process.StartInfo.Arguments = parameters;
process.StartInfo.UseShellExecute = false; // needs to be false in order to redirect output
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true; // redirect all 3, as it should be all 3 or none
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(toolFilePath);
process.StartInfo.Domain = domain;
process.StartInfo.UserName = userName;
process.StartInfo.Password = decryptedPassword;
process.Start();
output = process.StandardOutput.ReadToEnd(); // read the output here...
process.WaitForExit(); // ...then wait for exit, as after exit, it can't read the output
returnCode = process.ExitCode;
process.Close(); // once we have read the exit code, can close the process

Categories