|
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) |
| Class CommandLineArgs |
| Member | Parameters | Description |
| New | Args As
String(), ThirdFileName (opt), FourthFileName (opt) | Parse
an explicit argument array |
| New | ThirdFileName (opt),
FourthFileName (opt) | Parse from Environment.GetCommandLineArgs(),
skipping the exe path |
| Item(Key) | Key As
String | Default property; returns value or Nothing |
| Value | Key, DefaultValue (opt) | Returns value or a
caller-specified default |
| IntValue | Key,
DefaultValue (opt) | Parses as Integer; returns default if missing
or non-numeric |
| BoolValue | Key, DefaultValue
(opt) | True for "True"/"Yes"/"1"; False for "False"/"No"/"0" |
| Has | Key As String | Returns 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