none
Effective way to handle commandline parameters RRS feed

  • Question

  • Hi

    I'm looking for effective ways to deal with cmdline parameters. At the moment I create a class called AppParams that stores them for use in the application. Then in Program.cs I have a function that reads them and passes it to a constructor of the form. If the form doesn't get any parameters, it exists with an exit code of -1. The code I use in Program.cs is like the below:

            private static void GetCommandLineArgs()
            {
                string[] args = Environment.GetCommandLineArgs();
    
                if (args.Length > 1)
                {
                    for (int i = 1; i < args.Length; i++)
                    {
                        switch (args[i].Substring(0, 3))
                        {
                            case "/aa":
                                appParams.parm1 = args[i].Replace("/a:", "");
                                break;
                            case "/p:":
                                appParams.parm2 = args[i].Replace("/r:", "");
                                break;
                            case "/o:":
                                appParams.parm3 = args[i].Replace("/o:", "");
                                break;
                            case "/s:":
                                appParams.parm4 = args[i].Replace("/s:", "");
                                break;
                            case "/d:":
                                appParams.parm5 = args[i].Replace("/d:", "");
                                break;
                            case "/h:":
                                appParams.parm6 = args[i].Replace("/h:", "");
                                break;
                        }
                    }
                }
            }
    This way I look for strings of length 3 and replace the /a: with nothing and get the value. Is this a good way of doing it or not? It works but it always feels clumsy. What about having parameters that are required parameters? Also what if say if /a: is passed then /r: is required otherwise if /a: isn't passed then /r: is not required?

    This is just really a speculative request to see how people deal with them more than a "how to" request.

    Friday, March 1, 2019 10:49 AM

Answers

  • I've used both CommandLineParser before. Note that if you're targeting .NET Core IIRC it now ships with a command line parser as well.

    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by MrSnert Thursday, March 14, 2019 12:07 PM
    Friday, March 1, 2019 5:50 PM
    Moderator

All replies

  • Most people will use a third party library from NuGet. It is simpler if you need anything beyond the simplest checking for an argument or not. No reason to reinvent the wheel. As for cross-argument validation most people are storing the argument values into some sort of "settings" type. After the arguments have been parsed then validate the settings. IValidatableObject works well for this.

    Michael Taylor http://www.michaeltaylorp3.net

    Friday, March 1, 2019 2:55 PM
    Moderator
  • I'll be honest, I didn't even think of using a nuget package. Any recommendations?
    Friday, March 1, 2019 5:00 PM
  • A sample from MS :

    //-----------------------------------------------------------------------
    //  This file is part of the Microsoft .NET Framework SDK Code Samples.
    // 
    //  Copyright (C) Microsoft Corporation.  All rights reserved.
    // 
    //This source code is intended only as a supplement to Microsoft
    //Development Tools and/or on-line documentation.  See these other
    //materials for detailed information regarding Microsoft code samples.
    // 
    //THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
    //KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    //IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
    //PARTICULAR PURPOSE.
    //-----------------------------------------------------------------------
    
    /*=====================================================================
      File:      ArgParse.cs
    
      Summary:   Reusable class for parsing command-line arguments.
    
    =====================================================================*/
    
    // Add the classes in the following namespaces to our namespace
    using System;
    using System.Globalization;
    
    
    ///////////////////////////////////////////////////////////////////////////////
    namespace Microsoft.Samples
    {
    	public abstract class ArgumentParser
    	{
    		private String[] switchChars;             // For example: "/", "-"
    
    		private String[] switchSymbols;           // Switch character(s)
    
    		private Boolean caseSensitiveSwitches;   // Are switches case-sensitive?
    
    		// Domain of results when processing a command-line argument switch
    		protected enum SwitchStatus { NoError, Error, ShowUsage };
    
    		// Constructor that defaults to case-insensitive switches and 
    		// defaults to "/" and "-" as the only valid switch characters
    		protected ArgumentParser(String[] switchSymbols) 
            : this(switchSymbols, false, new string[] { "/", "-" })
    		{
    		}
    
    		// Constructor that defaults to "/" and "-" as the only valid switch characters
    		protected ArgumentParser(String[] switchSymbols, Boolean caseSensitiveSwitches) 
            : this(switchSymbols, caseSensitiveSwitches, new string[] { "/", "-" })
    		{
    		}
    
    		// Constructor with no defaults
    		protected ArgumentParser(String[] switchSymbols, Boolean caseSensitiveSwitches, String[] switchChars)
    		{
    			this.switchSymbols = switchSymbols;
    			this.caseSensitiveSwitches = caseSensitiveSwitches;
    			this.switchChars = switchChars;
    		}
    
    		// Every derived class must implement an OnUsage method
    		public abstract void OnUsage(String errorInfo);
    
    		// Every derived class must implement an OnSwitch method or a switch is considerred an error
    		protected virtual SwitchStatus OnSwitch(String switchSymbol, String switchValue)
    		{
    			return (SwitchStatus.Error);
    		}
    
    		// Every derived class must implement an OnNonSwitch method or a non-switch is considerred an error
    		protected virtual SwitchStatus OnNonSwitch(String value)
    		{
    			return (SwitchStatus.Error);
    		}
    
    		// The derived class is notified after all command-line switches have been parsed.
    		// The derived class can perform any sanity checking required at this time.
    		protected virtual SwitchStatus OnDoneParse()
    		{
    			// By default, we'll assume that all parsing was successful
    			return (SwitchStatus.Error);
    		}
    
    		// This Parse method always parses the application's command-line arguments
    		public Boolean Parse()
    		{
    			// Visual Basic will use this method since its entry-point function 
    			// doesn't get the command-line arguments passed to it.
    			return (Parse(Environment.GetCommandLineArgs()));
    		}
    
    		// This Parse method parses an arbitrary set of arguments
    		public Boolean Parse(String[] args)
    		{
    			SwitchStatus ss = SwitchStatus.NoError;	    // Assume parsing is sucessful.
    			int argNum;
    
    			for (argNum = 0; (ss == SwitchStatus.NoError) && (argNum < args.Length); argNum++)
    			{
    				// Determine if this argument starts with a valid switch character
    				Boolean fIsSwitch = false;
    
    				for (int n = 0; !fIsSwitch && (n < switchChars.Length); n++)
    				{
    					fIsSwitch = (0 == String.CompareOrdinal(args[argNum], 0, switchChars[n], 0, 1));
    				}
    
    				if (fIsSwitch)
    				{
    					// Does the switch begin with a legal switch symbol?
    					Boolean fLegalSwitchSymbol = false;
    					int n;
    
    					for (n = 0; !fLegalSwitchSymbol && (n < switchSymbols.Length); n++)
    					{
    						if (caseSensitiveSwitches)
    						{
    							fLegalSwitchSymbol = (0 == String.CompareOrdinal(args[argNum], 1, switchSymbols[n], 0, switchSymbols[n].Length));
    						}
    						else
    						{
    							fLegalSwitchSymbol = (0 == String.CompareOrdinal(args[argNum].ToUpper(CultureInfo.InvariantCulture), 1, switchSymbols[n].ToUpper(CultureInfo.InvariantCulture), 0, switchSymbols[n].Length));
    						}
    
    						if (fLegalSwitchSymbol) break;
    					}
    
    					if (!fLegalSwitchSymbol)
    					{
    						// User specified an unrecognized switch, exit
    						ss = SwitchStatus.Error;
    						break;
    					}
    					else
    					{
    						// This is a legal switch, notified the derived class of this switch and its value
    						ss = OnSwitch(caseSensitiveSwitches ? switchSymbols[n] : switchSymbols[n].ToLower(CultureInfo.InvariantCulture), args[argNum].Substring(1 + switchSymbols[n].Length));
    					}
    				}
    				else
    				{
    					// This is not a switch, notified the derived class of this "non-switch value"
    					ss = OnNonSwitch(args[argNum]);
    				}
    			}
    
    			// Finished parsing arguments
    			if (ss == SwitchStatus.NoError)
    			{
    				// No error occurred while parsing, let derived class perform a 
    				// sanity check and return an appropraite status
    				ss = OnDoneParse();
    			}
    
    			if (ss == SwitchStatus.ShowUsage)
    			{
    				// Status indicates that usage should be shown, show it
    				OnUsage(null);
    			}
    
    			if (ss == SwitchStatus.Error)
    			{
    				// Status indicates that an error occurred, show it and the proper usage
    				OnUsage((argNum == args.Length) ? null : args[argNum]);
    			}
    
    			// Return whether all parsing was sucessful.
    			return (ss == SwitchStatus.NoError);
    		}
    	}
    }
    
    ///////////////////////////////// End of File /////////////////////////////////


    Friday, March 1, 2019 5:12 PM
  • I've used both CommandLineParser before. Note that if you're targeting .NET Core IIRC it now ships with a command line parser as well.

    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by MrSnert Thursday, March 14, 2019 12:07 PM
    Friday, March 1, 2019 5:50 PM
    Moderator
  • Hi

    Is your problem solved? If so, please post "Mark as answer" to the appropriate answer, so that it will help other members to find the solution quickly if they face a similar issue.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Thursday, March 7, 2019 9:40 AM
    Moderator