how to parse main arguments? - c#

How can I find this information :
think we started this process :
testFile.exe i- 100 k- "hello" j-"C:\" "D:\Images" f- "true"
Now how can I get main argument when application started so I have :
int i = ... ; //i will be 100
string k = ... ; // k = hello
string[] path = ... ; // = path[0] = "C:\" , path[1] = "D:\Images"
bool f = ... ; // f = true;
regards

The arguments are passed to the Main function that is being called:
static void Main(string[] args)
{
// The args array contain all the arguments being passed:
// args[0] = "i-"
// args[1] = "100"
// args[2] = "k-"
// args[3] = "hello"
// ...
}
Arguments are in the same order as passed in the command line. If you want to use named arguments you may take a look at this post which suggests NDesk.Options and Mono.Options.

You can use Environment.CommandLine or Environment.GetCommandLineArgs()
String[] arguments = Environment.GetCommandLineArgs();
More info on MSDN

As already answered, you can use the string[] args parameter or Environment.GetCommandLineArgs(). Note that for CLickOnce deployed apps you need something else.
You can do your own processing on the string[] or use a library, like this one on CodePlex.
For some tricky details on spaces in filenames and escaping quotes, see this SO question.

You could use NDesk.Options. Here is their documentation.

Related

Unable to parse simple command line with commandlineparser

I have to parse an imposed command line which mix single/double dash args, args with = or as separator,etc (not very proper....)
The command line is like myexe.exe -Url=https://blabla.com/ --TestValue=val1 -monoapp -Ctx "default ctx" Open -Test2=CO0ZJP6f-ca
I'm trying to do that with commandlineparser.
(I suppose it's able to manage that, right ? )
So first I'm trying to parse the first parameter (myexe.exe -Url=https://blabla.com/).
public class CommandLineOptions
{
[Option("Url", Required = true)]
public string Url { get; set; }
}
....... In another file but in the same assembly
static void Main(string[] args) // args[0] = "-Url=https://blabla.com/"
{
var commandLineOptions = new CommandLineOptions();
var parseResult = Parser.Default.ParseArguments<CommandLineOptions>(args).WithParsed(result => commandLineOptions = result);
System.Console.WriteLine(parseResult.Tag); //NotParsed
System.Console.WriteLine(commandLineOptions.Url);
}
With that code, I have 2 errors CommandLine.MissingRequiredOptionError and CommandLine.UnknownOptionError.
(The MissingRequiredOptionError is produced beacause it cannot find the Url parameter)
So do you know where is my mistake ?
Thanks in advance for your help ;)
So final dev from commandlineparser said it isn't possible to parse it.
A single line option must be one char option.
The only way to bypass that is to preprocess the arguments.
I did this, it's working but it not allow short option composition (like in tar -xvf for example)
args = args.Select(arg => Regex.IsMatch(arg, "^-\\w{2,}") ? "-" + arg : arg ).ToArray();

pass parameter not having to worry about orders exe c#

Trying to pass a parameter without having to think about the order of parameters.
The first line is what I want to be able to do and below is what I am able to do.
What I want where both should give the same result
test.exe -word1 hello -word2 bye
test.exe -word2 bye -word1 hello
What I have
test.exe hello bye
static void Main(string[] args)
{
console.line(args[0])
console.line(args[1])
}
You could use the string input like so instead:
test.exe -word1=hello -word2=bye
test.exe -word2=bye -word1=hello
and then you could do something like this:
static void Main(string[] args)
{
// Single if the param is needed, else SingleOrDefault (to get null if the param. was not found
var word1 = args.Single(c => c.startsWith("-"+nameof(word1))
.Split(new char[] {'='})
.Last();
var word2 = args.Single(c => c.startsWith("-"+nameof(word2))
.Split(new char[] {'='})
.Last();
}
this is pseudo code, i did not run it - just to give you an example.
I wouldn't even bother tho, and just slap in: https://github.com/Tyrrrz/CliFx
You can use CommandLineApplication from https://www.nuget.org/packages/Microsoft.Extensions.CommandLineUtils
then you can configure your command line parameters and other available options and use them explicitly later on.
For example:
var app = new CommandLineApplication()
{
Description = "CLI tool for copying messages from one queue to another",
Name = "copy-queue",
};
var sourceOption = _app.Option("--source|-s", $"The source queue name", CommandOptionType.SingleValue);
var destinationOption = _app.Option("--destination|-d", $"The destination queue name", CommandOptionType.SingleValue);
var profileOption = _app.Option("--profile|-p", $"AWS CLI profile (default: default)", CommandOptionType.SingleValue);
var regionOption = _app.Option("--region|-r", $"AWS region (default: us-east-1)", CommandOptionType.SingleValue);
var batchSizeOption = _app.Option("--batch-size|-b", $"Batch size (default: 10)", CommandOptionType.SingleValue);
_app.HelpOption("--help|-h|-?");
var name = Assembly.GetEntryAssembly().GetName();
_app.VersionOption("--version", name.Name + " " + name.Version);
_app.Invoke = () => ExecuteAsync().Result;
try
{
return _app.Execute(args);
}
catch (CommandParsingException cpe)
{
Console.WriteLine(cpe.Message);
return 1;
}
I assume in your example word1 will have the value "hello", but could have any other value and you are interested into that value. With Array.IndexOf you can look for the index and then fetch the string with the index one higher:
int index1 = Array.IndexOf(args, "-word1");
string word1 = args[index1+1];
int index2 = Array.IndexOf(args, "-word2");
string word2 = args[index2+1];
You might consider to check also for the array boundaries (index1 < args.Length-1) and that index1 and index2 are not -1 and display an appropriate error message if necessary.

Call Process in C# with String args[]

I want to call a Process from C# with multiple parameters.
When i call:
ProcessStartInfo info = new ProcessStartInfo();
...
info.Arguments = "argument";
Process.Start(info);
i can only set a String as attribute. (Same to all types of the Start method)
Is there a way to set a String[] as arguments or how does this String getting interpreted?
Because on the other side
static void Main(string[] args)
i am getting a String[].
Thanks in advance.
Technically you can do it like that:
string[] args = new String[] {"argument1", "argument2", "argument3"};
...
info.Arguments = String.Join(" ", args);
the restriction is that args should not have arguments with spaces
Is there a way to set a String[] as argument?
No you can't do this, since the type of ProcessStartInfo.Arguments is string. Hence you can assign to it an array of strings.
You could pass to this string the parameters as follows:
info.Arguments = "argument1 argument2 argument3";
and your .exe will be executed as you were passing an array of strings with elements (argument1,argument2,argument3).

Extracting data from plain text string

I am trying to process a report from a system which gives me the following code
000=[GEN] OK {Q=1 M=1 B=002 I=3e5e65656-e5dd-45678-b785-a05656569e}
I need to extract the values between the curly brackets {} and save them in to variables. I assume I will need to do this using regex or similar? I've really no idea where to start!! I'm using c# asp.net 4.
I need the following variables
param1 = 000
param2 = GEN
param3 = OK
param4 = 1 //Q
param5 = 1 //M
param6 = 002 //B
param7 = 3e5e65656-e5dd-45678-b785-a05656569e //I
I will name the params based on what they actually mean. Can anyone please help me here? I have tried to split based on spaces, but I get the other garbage with it!
Thanks for any pointers/help!
If the format is pretty constant, you can use .NET string processing methods to pull out the values, something along the lines of
string line =
"000=[GEN] OK {Q=1 M=1 B=002 I=3e5e65656-e5dd-45678-b785-a05656569e}";
int start = line.IndexOf('{');
int end = line.IndexOf('}');
string variablePart = line.Substring(start + 1, end - start);
string[] variables = variablePart.Split(' ');
foreach (string variable in variables)
{
string[] parts = variable.Split('=');
// parts[0] holds the variable name, parts[1] holds the value
}
Wrote this off the top of my head, so there may be an off-by-one error somewhere. Also, it would be advisable to add error checking e.g. to make sure the input string has both a { and a }.
I would suggest a regular expression for this type of work.
var objRegex = new System.Text.RegularExpressions.Regex(#"^(\d+)=\[([A-Z]+)\] ([A-Z]+) \{Q=(\d+) M=(\d+) B=(\d+) I=([a-z0-9\-]+)\}$");
var objMatch = objRegex.Match("000=[GEN] OK {Q=1 M=1 B=002 I=3e5e65656-e5dd-45678-b785-a05656569e}");
if (objMatch.Success)
{
Console.WriteLine(objMatch.Groups[1].ToString());
Console.WriteLine(objMatch.Groups[2].ToString());
Console.WriteLine(objMatch.Groups[3].ToString());
Console.WriteLine(objMatch.Groups[4].ToString());
Console.WriteLine(objMatch.Groups[5].ToString());
Console.WriteLine(objMatch.Groups[6].ToString());
Console.WriteLine(objMatch.Groups[7].ToString());
}
I've just tested this out and it works well for me.
Use a regular expression.
Quick and dirty attempt:
(?<ID1>[0-9]*)=\[(?<GEN>[a-zA-Z]*)\] OK {Q=(?<Q>[0-9]*) M=(?<M>[0-9]*) B=(?<B>[0-9]*) I=(?<I>[a-zA-Z0-9\-]*)}
This will generate named groups called ID1, GEN, Q, M, B and I.
Check out the MSDN docs for details on using Regular Expressions in C#.
You can use Regex Hero for quick C# regex testing.
You can use String.Split
string[] parts = s.Split(new string[] {"=[", "] ", " {Q=", " M=", " B=", " I=", "}"},
StringSplitOptions.None);
This solution breaks up your report code into segments and stores the desired values into an array.
The regular expression matches one report code segment at a time and stores the appropriate values in the "Parsed Report Code Array".
As your example implied, the first two code segments are treated differently than the ones after that. I made the assumption that it is always the first two segments that are processed differently.
private static string[] ParseReportCode(string reportCode) {
const int FIRST_VALUE_ONLY_SEGMENT = 3;
const int GRP_SEGMENT_NAME = 1;
const int GRP_SEGMENT_VALUE = 2;
Regex reportCodeSegmentPattern = new Regex(#"\s*([^\}\{=\s]+)(?:=\[?([^\s\]\}]+)\]?)?");
Match matchReportCodeSegment = reportCodeSegmentPattern.Match(reportCode);
List<string> parsedCodeSegmentElements = new List<string>();
int segmentCount = 0;
while (matchReportCodeSegment.Success) {
if (++segmentCount < FIRST_VALUE_ONLY_SEGMENT) {
string segmentName = matchReportCodeSegment.Groups[GRP_SEGMENT_NAME].Value;
parsedCodeSegmentElements.Add(segmentName);
}
string segmentValue = matchReportCodeSegment.Groups[GRP_SEGMENT_VALUE].Value;
if (segmentValue.Length > 0) parsedCodeSegmentElements.Add(segmentValue);
matchReportCodeSegment = matchReportCodeSegment.NextMatch();
}
return parsedCodeSegmentElements.ToArray();
}

How do I launch a subprocess in C# with an argv? (Or convert agrv to a legal arg string)

I have a C# command-line application that I need to run in windows and under mono in unix. At some point I want to launch a subprocess given a set of arbitrary paramaters passed in via the command line. For instance:
Usage: mycommandline [-args] -- [arbitrary program]
Unfortunately, System.Diagnostics.ProcessStartInfo only takes a string for args. This is a problem for commands such as:
./my_commandline myarg1 myarg2 -- grep "a b c" foo.txt
In this case argv looks like :
argv = {"my_commandline", "myarg1", "myarg2", "--", "grep", "a b c", "foo.txt"}
Note that the quotes around "a b c" are stripped by the shell so if I simply concatenate the arguments in order to create the arg string for ProcessStartInfo I get:
args = "my_commandline myarg1 myarg2 -- grep a b c foo.txt"
Which is not what I want.
Is there a simple way to either pass an argv to subprocess launch under C# OR to convert an arbitrary argv into a string which is legal for windows and linux shell?
Any help would be greatly appreciated.
MSDN has a description of how the MS Visual C Runtime parses the string returned by GetCommandLine() into an argv array.
You might also be interested in the list2cmdline() function from the Python standard library that is used by Python's subprocess module to emulate the Unix argv behavior in a Win32 environment.
In windowsland, it's simple really...add extra quotation marks in the string you pass to the System.Diagnostics.ProcessStartInfo object.
e.g. "./my_commandline" "myarg1 myarg2 -- grep \"a b c\" foo.txt"
Thanks to all for the suggestions. I ended up using the algorithm from shquote (http://www.daemon-systems.org/man/shquote.3.html).
/**
* Let's assume 'command' contains a collection of strings each of which is an
* argument to our subprocess (it does not include arg0).
*/
string args = "";
string curArg;
foreach (String s in command) {
curArg = s.Replace("'", "'\\''"); // 1.) Replace ' with '\''
curArg = "'"+curArg+"'"; // 2.) Surround with 's
// 3.) Is removal of unnecessary ' pairs. This is non-trivial and unecessary
args += " " + curArg;
}
I've only tested this on linux. For windows you can just concatenate the args.
You will need to run a new subprocess using grep and all arguments that grep will be needing.
void runProcess(string processName, string args)
{
using (Process p = new Process())
{
ProcessStartInfo info = new ProcessStartInfo(processName);
info.Arguments = args;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
string output = p.StandardOutput.ReadToEnd();
// process output
}
}
then make a call to runProcess("grep", "a", "b", "c", "foo.txt");
Edit: Updated args handling.
Just use a Regex to check if a string has spaces of any kind, and replace the original string with a new one with quotes:
using System.Text.RegularExpressions;
// ...
for(int i=0; i<argv.Length; i++) {
if (Regex.IsMatch(i, "(\s|\")+")) {
argv[i] = "\"" + argv[i] + "\"";
argv[i].Replace("\"", "\\\"");
}
}

Categories