Home > C# > C#: Simple Command Line Arguments Parser

C#: Simple Command Line Arguments Parser

    Working with SharePoint applications I very often develop small console applications for adjusting lists, list items, content types and so on. To pass some parameters into those utilities I employ the command line arguments. Then inside applications I deal with an array of arguments – string[] args:

class Program
{
    static void Main(string[] args)
    {
        // get passed parameters from args
    }
}

I usually use the following notation: -paramName paramValue. Here a parameter name with the leading minus sign (‘-’) is followed by a parameter value. If the presence of value isn’t assumed, only -paramName is used. For example, the following command line

-url "http://dotnetfollower.com" -useElevatedPrivileges

directs an utility to “process the web site http://dotnetfollower.com and use the elevated objects for that”.

To simplify parameters fetching from the args array I’ve implemented an auxiliary class – InputArguments, which parses the arguments and fills a dictionary out with proper key-value pairs. The dictionary allows accessing a parameter value in the way like this: InputArguments["-url"] or InputArguments["url"]. The source code of the InputArguments class is listed below:

using System;
using System.Collections.Generic;

namespace Common
{
    public class InputArguments
    {
        #region fields & properties
        public const string DEFAULT_KEY_LEADING_PATTERN = "-";

        protected Dictionary<string, string> _parsedArguments   = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        protected readonly string            _keyLeadingPattern;

        public string this [string key]
        {
            get { return GetValue(key); }
            set
            {
                if (key != null)
                    _parsedArguments[key] = value;
            }
        }
        public string KeyLeadingPattern
        {
            get { return _keyLeadingPattern; }
        }
        #endregion

        #region public methods
        public InputArguments(string[] args, string keyLeadingPattern)
        {
            _keyLeadingPattern = !string.IsNullOrEmpty(keyLeadingPattern) ? keyLeadingPattern : DEFAULT_KEY_LEADING_PATTERN;

            if (args != null && args.Length > 0)
                Parse(args);
        }
        public InputArguments(string[] args) : this(args, null)
        {
        }

        public bool Contains(string key)
        {
            string adjustedKey;
            return ContainsKey(key, out adjustedKey);
        }

        public virtual string GetPeeledKey(string key)
        {
            return IsKey(key) ? key.Substring(_keyLeadingPattern.Length) : key;
        }
        public virtual string GetDecoratedKey(string key)
        {
            return !IsKey(key) ? (_keyLeadingPattern + key) : key;
        }
        public virtual bool IsKey(string str)
        {
            return str.StartsWith(_keyLeadingPattern);
        }
        #endregion

        #region internal methods
        protected virtual void Parse(string[] args)
        {
            for (int i = 0; i < args.Length; i ++)
            {
                if(args[i] == null) continue;

                string key = null;
                string val = null;

                if(IsKey(args[i]))
                {
                    key = args[i];

                    if(i + 1 < args.Length && !IsKey(args[i + 1]))
                    {
                        val = args[i + 1];
                        i ++;
                    }
                }
                else
                    val = args[i];

                // adjustment
                if (key == null)
                {
                    key = val;
                    val = null;
                }
                _parsedArguments[key] = val;
            }
        }

        protected virtual string GetValue(string key)
        {
            string adjustedKey;
            if(ContainsKey(key, out adjustedKey))
                return _parsedArguments[adjustedKey];

            return null;
        }

        protected virtual bool ContainsKey(string key, out string adjustedKey)
        {
            adjustedKey = key;

            if (_parsedArguments.ContainsKey(key))
                return true;

            if (IsKey(key))
            {
                string peeledKey = GetPeeledKey(key);
                if(_parsedArguments.ContainsKey(peeledKey))
                {
                    adjustedKey = peeledKey;
                    return true;
                }
                return false;
            }

            string decoratedKey = GetDecoratedKey(key);
            if(_parsedArguments.ContainsKey(decoratedKey))
            {
                adjustedKey = decoratedKey;
                return true;
            }
            return false;
        }
        #endregion
    }
}

As you can see, the InputArguments stores the parsed parameters and theirs values in the _parsedArguments dictionary, which is accessible through the special indexer. Pay attention to the ContainsKey methods, which attempts different variations (with or without leading symbols) of parameter name in case the passed name isn’t presented among the stored arguments. So, passing the

-url "http://dotnetfollower.com" -useElevatedPrivileges

arguments into an application, the InputArguments can be used as follows:

static void Main(string[] args)
{
    InputArguments arguments = new InputArguments(args);

    Console.WriteLine(arguments["-url"]);
    if (arguments.Contains("useElevatedPrivileges"))
        Console.WriteLine("useElevatedPrivileges is set");
}

The above mentioned command line arguments will be turned into the following key-value pairs:

{["-url", "http://dotnetfollower.com"]}
{["-useElevatedPrivileges", null]}

Below is the more complex command line borrowed from the WSPBuilder and slightly modified to have orphaned values without a foregoing parameter name:

"some orphaned value" -ExpandTypes false -BuildSafeControls true
-WSPName mySPSolution.wsp  -Outputpath "C:\WSPDeployment\myApp"
-SolutionId d403bb18-c5f2-4b43-9d55-12b256a6295a
-SolutionPath "C:\WSPDeployment\myApp" -TraceLevel Verbose
-DLLReferencePath "C:\WSPDeployment\ReferencedAssemblies"

This arguments will be turned into the following:

{["some orphaned value", null]}
{["-ExpandTypes", "false"]}
{["-BuildSafeControls", "true"]}
{["-WSPName", "mySPSolution.wsp"]}
{["-Outputpath", "C:\WSPDeployment\myApp"]}
{["-SolutionId", "d403bb18-c5f2-4b43-9d55-12b256a6295a"]}
{["-SolutionPath", "C:\WSPDeployment\myApp"]}
{["-TraceLevel", "Verbose"]}
{["-DLLReferencePath", "C:\WSPDeployment\ReferencedAssemblies"]}

Frankly speaking, I don’t use the InputArguments class directly. For every utility I create a derived class to provide an easy access to the values of parameters specific for that particular application. For example, the derived class employing the -url and -useElevatedPrivileges may look like the following:

public class UtilityArguments : InputArguments
{
    public bool UseElevatedPrivileges
    {
        get { return GetBoolValue("-useElevatedPrivileges"); }
    }

    public string Url
    {
        get { return GetValue("url"); }
    }

    public UtilityArguments(string[] args) : base(args)
    {
    }

    protected bool GetBoolValue(string key)
    {
        string adjustedKey;
        if (ContainsKey(key, out adjustedKey))
        {
            bool res;
            bool.TryParse(_parsedArguments[adjustedKey], out res);
            return res;
        }
        return false;
    }
}

It can be used as follows:

static void Main(string[] args)
{
    UtilityArguments arguments = new UtilityArguments(args);

    Console.WriteLine("Url: " + arguments.Url);
    Console.WriteLine("UseElevatedPrivileges: " + arguments.UseElevatedPrivileges);
}

If you prefer using another sign or group of signs before parameter name in command line, use the proper constructor of the InputArguments class

static void Main(string[] args)
{
    InputArguments arguments = new InputArguments(args, "/");
    // ...
}

or

public class MyArguments : InputArguments
{
    // ...
    public MyArguments(string[] args) : base(args, "--")
    {
    }
    // ...
}

 
Categories: C# Tags: ,
  1. November 11th, 2012 at 05:50 | #1

    Have you seen http://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser ?
    Is your class better? If yes, what are the benefits to use your class?

    • Administrator
      November 15th, 2012 at 11:17 | #2

      @michael.freidgeim
      Hello!
      The parser you mentioned about is very simple as well. It uses Regex, so I think it’s a bit slower than mine (but, dealing with ordinary command lines, it’s never even noticeable). On the other hand it allows to have different leading characters for keys in one command line, however, to be honest, I don’t know any app which uses such confusion of key’s prefixes at the same time. I usually define only one leading pattern for keys per app. In short, I’m not ready to say which parser is better, they are a bit different. But I believe both do their work well.

  2. Masha
    March 1st, 2013 at 05:49 | #3

    simple & useful. nice

  3. Bart
    February 22nd, 2014 at 14:57 | #4

    Thanks a lot! Exactly what I was looking for!!!

  4. nikopol
    March 6th, 2014 at 09:28 | #5

    Thank you very much, makes also some very good learning.

  1. No trackbacks yet.