5

I'd like my console app to use hyphen-named inputs, like so:

myApp -S "myServerName" -D "myDatabaseName"

instead of the usual:

myApp "myServerName" "myDatabaseName"

I think the first is more friendly to developers that want to use my console app down the road. I think that I don't know what this type of input is called, else I woulda found it on google.

Nifle
  • 11,745
  • 10
  • 75
  • 100
jcollum
  • 43,623
  • 55
  • 191
  • 321

4 Answers4

3

Everything that is entered after the executable name is called a command line argument.

But whether you use dashes or slashes or some other kind of keyword is completely up to the application to implement.

If you want to save yourself some effort, you might want to use an command line parser.

Here is an open source one that you can use. http://www.codeplex.com/cmdline

Mark Arnott
  • 1,975
  • 3
  • 17
  • 28
2

I like using NDesk.Options. You just have to add that source file to your project. It's really easy to use and has good documentation. The code would look like:

string server = null;
string database = null;
var p = new OptionSet () {
    { "S",  v => server = v },
    { "D",  v => database = v },
   };
List<string> extra = p.Parse (args); // 'args' comes from Main (string [] args)
...

If you want automatic help to be generated, you would do:

string server = null;
string database = null;
var p = new OptionSet () {
    { "S", "Server name", v => server = v },
    { "D", "Database name", v => database = v },
    { "h", "Display help", v => { show_help = true; }},
   };
List<string> extra = p.Parse (args); // 'args' comes from Main (string [] args)
if (show_help) {
   Console.WriteLine ("Name of your program and brief description");
   p.WriteOptionDescriptions (Console.Out);
   Environment.Exit (0);
}
...
Gonzalo
  • 20,805
  • 3
  • 75
  • 78
  • Uhhh: "The NDesk.Options.dll assembly is currently UNSTABLE. Please provide any feedback on the API and design of the library. " (from the site you listed, front page) – jcollum Dec 21 '09 at 20:22
  • In this case, unstable means that the API might change, not that it does not work and since you can copy the single file to your project, API changes should not affect you. – Gonzalo Dec 21 '09 at 20:48
1

Put this in your arg-processing method:

    for (int i=0; i < args.Length; i++)
    {
        switch (args[i])
        {
            case "-f":  // string data
                i++;
                if (args.Length <= i) throw new ArgumentException(args[i]);
                _file = args[i];
                break;

            case "-p":  // boolean flag
                _pflag= true;
                break;

            case "-i":  // int argument, allows hex or decimal
                i++;
                if (args.Length <= i) throw new ArgumentException(args[i]);
                if (_intParam != DefaultIntParamValue)
                    throw new ArgumentException(args[i]);
                if (args[i].StartsWith("0x"))
                    _intParam = System.Int32.Parse(args[i].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier );
                else
                    _intParam = System.Int32.Parse(args[i]);
                break;


            case "-s":  // size, in bytes, K, MB, GB
                i++;
                if (args.Length <= i) throw new Exception(args[i-1]);
                if (args[i].ToUpper().EndsWith("K"))
                    _size = System.Int32.Parse(args[i].Substring(0,args[i].Length-1)) * 1024;
                else if (args[i].ToUpper().EndsWith("KB"))
                    _size = System.Int32.Parse(args[i].Substring(0,args[i].Length-2)) * 1024;
                else if (args[i].ToUpper().EndsWith("M"))
                    _size = System.Int32.Parse(args[i].Substring(0,args[i].Length-1)) * 1024*1024;
                else if (args[i].ToUpper().EndsWith("MB"))
                    _size = System.Int32.Parse(args[i].Substring(0,args[i].Length-2)) * 1024*1024;
                else
                    _size = Int32.Parse(args[i]);
                break;


            case "-?":
                throw new ArgumentException(args[i]);

            default:  // positional argument
                if (_positionalArg != null)
                    throw new ArgumentException(args[i]);

                _positionalArg = args[i];
                break;
        }
    }

I often put this into the constructor of a "runner" type.
This logic assumes the arg processing is done inside a try..catch, and in the catch, the Usage is displayed. Like this:

try 
{
    MyClass x = new MyClass(args);
    x.DoTheThing(); 
}
catch (Exception exc1)
{
   Usage(); 
}
Cheeso
  • 189,189
  • 101
  • 473
  • 713
1

You could write a method that converts a string[] into a Dictionary<string, string>:

public static class NamedArgs
{
    public static IDictionary<string, string> ToDictionary(string[] args)
    {
        var dict = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

        for (int i = 0; i < args.Length; i++)
            if (args[i].StartsWith("-"))
            {
                if (i + 1 >= args.Length || args[i + 1].StartsWith("-"))
                    dict.Add(args[i], string.Empty);
                else
                    dict.Add(args[i], args[++i]);
            }

        return dict;
    }
}

It would be used like this:

var namedArgs = NamedArgs.ToDictionary(args);
        
if(namedArgs.TryGetValue("-s", out string myServerName) == false)
    throw new ArgumentNullException("-s");

Console.WriteLine($"Selected server: '{myServerName}'");
Ricardo Valente
  • 581
  • 1
  • 10
  • 14