Process.StartInfo with '*' character - finding distro of linux - c#

I have a little problem with star character in my command for linux - I need to find out distro. When I replace this character witch e.g. 'fedora' then this command gives good results. In this case it write something like this: /bin/cat: /etc/*-release: Directory or file doesn't exist.
My code is:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cat";
p.StartInfo.Arguments = "/etc/*-release";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Thanks in advance for your answer.
Matej

When you run cat /etc/*-release from your shell, the shell is responsible for expanding the * to a list of matching files, if any.
When you directly execute a program yourself (as you're doing here with the Process interface), you need to re-create the shell's behavior yourself. This is actually for the best, as it is a bit silly to run cat to read a file from a full-featured programming language -- surely the language provides something easy for the logical equivalent of:
list = glob(/etc/*-release)
foreach file in (list):
fd = open(file, "read");
contents = read(fd)
close(fd)
dosomething_with(contents)
You can use whatever mechanism you like to replace the glob() bit there; a fellow stacker has provided his own glob() mechanism in another answer.

Related

How do I create a string dynamically in C#?

I am developing an application for managing Personal Hotspot of a Laptop
(Windows of Course).
I'm having a little difficulty in changing the Hotspot Name.
Here is my Code:
//For CMD Command
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new
System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.FileName = "CMD.exe";
startInfo.CreateNoWindow = true;
//Reading User Input in textBox1:
string name = textBox1.Text;
//CMD Argument:..., Which is currently written wrong to make it Easy to
//understand the problem
startInfo.Arguments = "/C netsh wlan set hostednetwork ssid="name";
process.StartInfo = startInfo;
process.Start();
Here, In the Arguments line, the syntax is wrong. The value assigned to Arguments should be a single string. I need to incorporate name, that has a dynamic value, with the rest of the string that is constant. How do I do that?
Unless I'm missing something here, seems to me that a simple string concatenation would do it:
startInfo.Arguments = "/C netsh wlan set hostednetwork ssid=" + name;
There are several ways you can create a dynamic string:
For your specific case, Concatenation is the best choice as your string is very simple.The reason you would choose this method for your case is because it is very light as compared to other methods and has a clean enough syntax.
startInfo.Arguments = "/C netsh wlan set hostednetwork ssid=" + name;
For anything more complicated, string.Format is a popular go to. You would generally use it to combine more complex strings than your example. This tutorial covers the subject thoroughly.
startInfo.Arguments = string.Format("/C netsh wlan set hostednetwork ssid={0}", name);
The release of C#6.0 included a neat feature: interpolated strings. It is for the most part just syntactic sugar for string.Format, where the line below is turned into the line above at compile time. There are subtle differences, but those are not important in this thread.
startInfo.Arguments = $"/C netsh wlan set hostednetwork ssid={name}";
And lastly, if you need to change a string more than a few times (I usually use the rule of 5 - i.e. a string changes more than 5 times), I would use the StringBuilder class. A great application, among many others, would be a long loop that modifies a certain string each iteration. See This Tutorial.
In this case, the Garbage Collector will thank you for a using a StringBuilder!

My command which is being produced by my C# code isn't working in C#, but works perfectly when I paste it to cmd

It's all about a line I want to use to get windows update information, which is part of wmic.
My code looks like this:
Process p = new Process();
string arguments = "qfe list full /format:htable > "+ path;
ProcessStartInfo procStartInfo = new ProcessStartInfo("wmic", arguments);
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
procStartInfo.UseShellExecute = false;
p.StartInfo = procStartInfo;
p.Start();
while path is the valid location where the file would be dumped, ending with a hotfixlog.htm of course.
The problem is, that nothing happens at all. However, when I take the final product from the arguments-variable, and paste it manually into cmd with 'wmic < variablecontent >' it's working perfectly fine and I end up with the .htm I expect.
The line created looks like this:
"qfe list full /format:htable > C:\Users\...\WindowsHotfixes.htm"
What do I have to change to make it work from the code? I was expecting the backslashes to cause problems, but when manually entering the line they don't.
Your code will not work because the redirection operator (>) is not an element of the OS available to any application, but an operator in cmd.exe. It works in the command line because cmd is handling it, but wmic doesn't know what to do with it.
You can use the redirection if your command line is something like
cmd /c"wmic qfe list full /format:htable > x:\somewhere\file.htm"
Or you can remove the redirection and indicate to wmic that you want the data saved in a file
wmic /output:"x:\somewhere\file.htm" qfe list full /format:htable

strange behaviour on another process via Process.Start(startInfo)

Our C# (V3.5) application needs to call another C++ executable which is from another company. we need to pass a raw data file name to it, it will process that raw data (about 7MB) file and generate 16 result files (about 124K for each).
The code to call that executable is this:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = exePath;
startInfo.Arguments = rawDataFileName;
try
{
Process correctionProcess = Process.Start(startInfo);
correctionProcess.WaitForExit();
}
catch(nvalidOperationException ex)
{
....
}
catch(...)
...
It works fine. Now we have new raw data. After replace the old raw data with the new raw data file. That executable process never return to us. It will hang forever. If we kill our C# application, those result files will be generated in the target directoy. It looks like the executable does create those result files but has issue to write to the disk and return to us until the process is terminated.
It is NOT like this with the old raw data file.
When we run the executable with the new raw data directly (no from our C# app call), it works fine. This means this executable has no problem with the new raw data.
My question 1: what's the possible causes for this behaviour?
Now I change our code with startInfo.UseShellExecute = true; and add startInfo.WorkingDirectory= ..., and disabled
//startInfo.RedirectStandardError = true;
//startInfo.RedirectStandardOutput = true;
Then it works.
My question 2: why use Windows Shell solve this issue?
My question 3: why it works before without using Shell?
My question 4: when we should use Shell and When shouldn't?
thanks,
Several possibilities:
You are redirecting output and error but not reading it. The process will stall when its stdout or stderr buffer fills up to capacity.
The program might be displaying an error message and waiting for a keypress. You are not redirecting input nor check stderr, that keypress will never come.
Some programs, xcopy.exe is a very good example, require stdin to be redirected when you redirect stdout. Although the failure mode for xcopy.exe is an immediate exit without any diagnostic.
Seeing it fixed when you kill your C# program makes the first bullet the likeliest reason.
I know this, it is a very common problem. I has to do with the output, which must be handled asynchronously. You just can't WaitForExit when output exceeds certain amount of data.
You need to add
myStdErr= correctionProcess.StandardError.ReadToEnd();
Only once usually works, if you want to overkill this works ("P" being my Process)
while (!P.HasExited)
stdErr+= P.StandardError.ReadToEnd();
If you don't need the stdout/stderr, just turn the Redirect* properties to false.

Perl Script Output Capture Problem using C#

I was following one of the thread to run perl scripts from my c# program.
My c# code is like this:
private void RunScript(ArrayList selectedScriptFileList)
{
foreach (var curScriptFileName in selectedScriptFileList)
{
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("perl.exe");
myProcessStartInfo.Arguments = (string)(curScriptFileName);
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcessStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProcessStartInfo.CreateNoWindow = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
myProcess.WaitForExit();
string output = myProcess.StandardOutput.ReadToEnd();
this.ScriptTestResultTextBox.AppendText(output);
}
}
And my perl script requires XML parsing. I can read the print statement before the XML parsing, but not after the parsing starts. The script runs find on DoS shell.
Here is part of my script:
print("\n");
print("****************** test1.pl ***********************\n");
print("\n");
print("1");
print("2");
my $scriptName = 'test1.pl';
my $file = '../../ScriptParamLib.xml';
my $parser = XML::LibXML->new();
my $tree = $parser->parse_file($file);
my $root = $tree->getDocumentElement;
my #species = $root->getElementsByTagName('test_node');
print("Accessing XML Data Base...\n");
The c# testbox only shows the first three print statement but not the last one.
Does anybody knows why?
Thanks
You could add more debugging print statements (e.g. one between every other line of your code) to see how far the execution gets. However, I'm going to go on a hunch and suggest that adding these three lines to your script will either solve the problem outright or lead you closer to a solution:
use strict;
use warnings;
use XML::LibXML;
Please update your question indicating how far execution gets and what errors you see!
I figured I should roll my comments into an answer since they proved to be helpful:
Since using an absolute path for $file in the Perl script works, the issue most likely has something to do with the working directory of the process that gets spawned from the C# program. You can use the Cwd module in the Perl script to see what the working directory actually is. If it's not what you expect, try setting it via the WorkingDirectory property of ProcessStartInfo in your C# program. Relative paths should work just fine after that.

c# redirect (pipe) process output to another process

I am trying to run a process in c# using the Process class.
Process p1 = new process();
p1.startinfo.filename = "xyz.exe";
p1.startinfo.arguments = //i am building it based on user's input.
p1.start();
So based on user input i am building the argument value. Now i have a case where i have to pipe the output of p1 to another process say grep. so i basically tried this
p1.startinfo.arguments = "-info |grep 1234" ;
what i intended is something like xyz.exe -info|grep 1234
but this doesn't seem to work in .net .. I can actually create another process variable and run "grep" as a separate process.. But i was wondering if there is any way to do as iam trying out above..
The much easier way would be to do just use cmd as your process.
Process test = new Process();
test.StartInfo.FileName = "cmd";
test.StartInfo.Arguments = #"/C ""echo testing | grep test""";
test.Start();
You can capture the output or whatever else you want like any normal process then. This was just a quick test I built, but it works outputting testing to the console so I would expect this would work for anything else you plan on doing with the piping. If you want the command to stay open then use /K instead of /C and the window will not close once the process finishes.

Categories