A Million Little Pieces Of My Mind

CommandLineArgs

By: Paul S Cilwa Posted: 5/1/2020 Unpublished Page Views: 133
Hashtags: #Namtira #VisualBasic #VBNET #ClassLibrary #NamtiraLib #CommandLineArgs #CommandLine #Arguments #Parser
A command-line argument parser that maps positional files and named parameters into a case-insensitive dictionary.
Estimated reading time: 5 minute(s) (1044 words)

Every command-line tool needs to turn that messy array of strings into something useful, and CommandLineArgs does exactly that. It parses positional file paths, named parameters, and boolean flags into a single case-insensitive dictionary--so your tool can focus on its actual job instead of string-splitting.

Class CommandLineArgs
MemberParametersDescription
NewArgs As String(), ThirdFileName (opt), FourthFileName (opt)Parse an explicit argument array
NewThirdFileName (opt), FourthFileName (opt)Parse from Environment.GetCommandLineArgs(), skipping the exe path
Item(Key)Key As StringDefault property; returns value or Nothing
ValueKey, DefaultValue (opt)Returns value or a caller-specified default
IntValueKey, DefaultValue (opt)Parses as Integer; returns default if missing or non-numeric
BoolValueKey, DefaultValue (opt)True for "True"/"Yes"/"1"; False for "False"/"No"/"0"
HasKey As StringReturns True if the parameter exists
HelpRequested--True if -Help, -?, or /? was specified
IsEmpty--True if no arguments were parsed
Count--Number of parsed arguments
InputFile--First positional argument, or Nothing
OutputFile--Second positional argument, or Nothing
All--Copy of the internal dictionary
ToString--Diagnostic summary of all parsed arguments

How It Works

The format is straightforward:

MyTool.exe photo.jpg output.png -Width=800 -Grayscale

Bare arguments (no leading dash) are treated as positional file paths. The first becomes InputFile, the second OutputFile, and any beyond that get names like ThirdFile and FourthFile--or whatever you pass as the ThirdFileName and FourthFileName constructor parameters.

Dashed arguments are named parameters. If they contain an =, the left side is the key and the right side is the value: -Width=800 stores "Width""800". If there is no =, the argument is a boolean flag: -Grayscale stores "Grayscale""True".

All three prefix styles work interchangeably: -Width=800, /Width=800, and --Width=800 all produce the same result. The flag ? is automatically mapped to Help, so -?, /?, -Help, and /Help all set the same flag. Use the HelpRequested property for a clean check. Parameter names are always case-insensitive, and quoted values are automatically dequoted via NamtiraLib's Dequote extension method.

Constructors

The primary constructor takes an explicit String() array, which you would typically get from My.Application.CommandLineArgs or a test harness. The parameterless overload grabs the arguments from Environment.GetCommandLineArgs() automatically, skipping the first element (the executable path).

' Parse from an explicit array Dim Args As New CommandLineArgs(My.Application.CommandLineArgs.ToArray()) ' Parse from Environment automatically Dim Args As New CommandLineArgs() ' Customize positional names for a tool that takes three input files Dim Args As New CommandLineArgs( My.Application.CommandLineArgs.ToArray(), ThirdFileName:="MaskFile")

Retrieving Values

The default Item property lets you use indexer syntax directly on the object. The typed accessors--IntValue and BoolValue--save you from writing the same parsing boilerplate in every tool.

Dim Args As New CommandLineArgs() ' Positional shortcuts Dim Source As String = Args.InputFile ' first bare arg Dim Dest As String = Args.OutputFile ' second bare arg ' Named parameter with default Dim Width As Integer = Args.IntValue("Width", 1024) ' Boolean flag If Args.BoolValue("Grayscale") Then ' convert to grayscale End If ' Check whether a parameter was supplied at all If Args.Has("Format") Then Dim Fmt As String = Args("Format") End If ' Diagnostic dump Console.WriteLine(Args.ToString()) ' Output: InputFile=photo.jpg, OutputFile=output.png, Width=800, Grayscale=True

Positional Overflow

If a command line has more positional arguments than named slots, the extras are stored with keys like Positional4, Positional5, and so on. You can retrieve them via the All property or by key name.

Source

The full class lives in NamtiraLib/My Project/15.SystemTools/CommandLine.vb.

Public Class CommandLineArgs Private ReadOnly _Args As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase) Private ReadOnly _PositionalNames As String() Public Sub New(Args As String(), Optional ThirdFileName As String = "ThirdFile", Optional FourthFileName As String = "FourthFile") _PositionalNames = {"InputFile", "OutputFile", ThirdFileName, FourthFileName} Dim PositionalIndex As Integer = 0 For Each Arg As String In Args If Arg.StartsWith("-") OrElse Arg.StartsWith("/") Then Dim Content As String = Arg.Substring(1) If Content.StartsWith("-") Then Content = Content.Substring(1) Dim EqPos As Integer = Content.IndexOf("="c) If EqPos > 0 Then Dim Key As String = Content.Substring(0, EqPos).Trim() Dim Value As String = Content.Substring(EqPos + 1).Dequote() _Args(Key) = Value Else _Args(Content.Trim()) = "True" End If Else Dim Value As String = Arg.Dequote() If PositionalIndex < _PositionalNames.Length Then _Args(_PositionalNames(PositionalIndex)) = Value Else _Args("Positional" & PositionalIndex.ToString()) = Value End If PositionalIndex += 1 End If Next End Sub Public Sub New(Optional ThirdFileName As String = "ThirdFile", Optional FourthFileName As String = "FourthFile") Me.New(Environment.GetCommandLineArgs().Skip(1).ToArray(), ThirdFileName, FourthFileName) End Sub Default Public ReadOnly Property Item(Key As String) As String Get Dim Value As String = Nothing _Args.TryGetValue(Key, Value) Return Value End Get End Property Public Function Value(Key As String, Optional DefaultValue As String = Nothing) As String Dim Result As String = Nothing If _Args.TryGetValue(Key, Result) Then Return Result Return DefaultValue End Function Public Function IntValue(Key As String, Optional DefaultValue As Integer = 0) As Integer Dim S As String = Me(Key) If S Is Nothing Then Return DefaultValue Dim Result As Integer If Integer.TryParse(S, Result) Then Return Result Return DefaultValue End Function Public Function BoolValue(Key As String, Optional DefaultValue As Boolean = False) As Boolean Dim S As String = Me(Key) If S Is Nothing Then Return DefaultValue Select Case S.ToLower() Case "true", "yes", "1" : Return True Case "false", "no", "0" : Return False Case Else : Return DefaultValue End Select End Function Public Function Has(Key As String) As Boolean Return _Args.ContainsKey(Key) End Function Public ReadOnly Property IsEmpty As Boolean Get Return _Args.Count = 0 End Get End Property Public ReadOnly Property Count As Integer Get Return _Args.Count End Get End Property Public ReadOnly Property InputFile As String Get Return Me("InputFile") End Get End Property Public ReadOnly Property OutputFile As String Get Return Me("OutputFile") End Get End Property Public ReadOnly Property All As Dictionary(Of String, String) Get Return New Dictionary(Of String, String)(_Args, StringComparer.OrdinalIgnoreCase) End Get End Property Public Overrides Function ToString() As String If _Args.Count = 0 Then Return "(no arguments)" Dim SB As New System.Text.StringBuilder() For Each KV In _Args If SB.Length > 0 Then SB.Append(", ") SB.Append(KV.Key & "=" & KV.Value) Next Return SB.ToString() End Function End Class