- Offizieller Beitrag
Angeregt durch die Kommandozeilenbehandlung in Nim habe ich das dortige "parseopt" Modul als Vorlage für eine ähnliche Umsetzung in AutoIt genutzt.
Das ist nicht interessant für 2...3 Parameter in der Kommandozeile. Wer aber reine CUI-Anwendungen erstellt, wird u.U. eine Vielzahl möglicher Parameter bereitstellen wollen. Und da ist die in Nim genutzte Variante recht elegant und vielfältig. Für eine angenehmere Syntax habe ich Strukturen verwendet.
CMD-LINE REGELN
Grundlagen: • Parameter werden getrennt mit einem Leerzeichen. (key=val key2=val key3=val)
• Werte, die Leerzeichen enthalten, sind in Stringbegrenzer einzufassen.
(key="val with space")
• Zur Wertzuweisung können "=" oder ":" genutzt werden. (key=val | key:val)
• Nur für Zuweisungen mit ":" ist ein optionales Leerzeichen vor dem Wert möglich.
(key:val | key: val)
Typen: CMD_SHORTOPTION
• Beginnt mit "-" (-k=val)
• Der Name (key) hat eine Länge von nur 1 Zeichen!!
Wird ein längerer Name übergeben, wird dieser ignoriert, mit einer Ausnahme:
Sind im übergebenen Wert Leerzeichen enthalten, wird der Teil hinter dem ersten Leerzeichen erkannt als CMD_ARGUMENT,
gebildet aus den folgenden Nicht-Leerzeichen.
• [SONDERFALL] "-key"
Ein CMD_SHORTOPTION Parameter ohne Zuweisung darf länger sein als 1 Zeichen.
CMD_LONGOPTION
• Beginnt mit "--" (--key=val)
• Der Name hat eine Länge von mindestens 1 Zeichen.
• [SONDERFALL] "--" alleine
Die "--" Option wird üblicherweise verwendet, um alle folgenden Parameter als Argument zu markieren, unabhängig von
den dort verwendeten Zeichen. Es wird interpretiert als CMD_LONGOPTION und der Name ist ein Leerstring.
CMD_ARGUMENT
• Alle anderen Nicht-Leerzeichen (arg | /abc)
Optionen: Parameter können deklariert werden als "ParamNoVal". Das bedeutet, dass nur ein Key erwartet wird.
Da der Parameter keinen Wert hat, ist dieser immer ein Boolscher Parameter (übergeben oder nicht).
Wird trotzdem ein Wert zugewiesen, wird dieser ignoriert.
Um einen Parameter als "ParamNoVal" zu deklarieren, wird der Parser mit einer Liste dieser Parameter initialisiert.
Eine Liste für ShortNoVal und eine weitere für LongNoVal.
FUNCTIONEN:
_CmdLineParser_Init()
Initialisiert den Parser. Liest die Kommandozeile und erstellt eine eigene Struktur für jeden Parameter.
Gibt eine Struktur zurück mit:
.cmdline Die gesamte Parameterzeile
.cmdcount Die Anzahl der Parameter
_CmdLineParser_Next()
Gibt die nächste Parameter Struktur vom Stapel zurück.
Die Struktur der Parameter:
.index Der 0-basierte Index des Parameters
.pos Start Position des Parameters in der Parameterzeile
.kind $CMDLINEPARSE_ARGUMENT | $CMDLINEPARSE_LONGOPTION | $CMDLINEPARSE_SHORTOPTION
.param Der gesamte Parameter, z.B.: --file="my file path"
.key Der Key, z.B.: file
.val Der Wert, z.B.: "my file path"
_CmdLineParser_GetParamByKey()
Gibt die Parameter Struktur für einen übergebenen Key zurück.
_CmdLineParser_GetParamByIndex()
Gibt die Parameter Struktur für einen übergebenen Index zurück. (0-basiert)
Alles anzeigen
Für die Funktion _CmdLineParser_Next() habe ich einen Iterator implementiert, sodass bei jedem Aufruf der Folgeparameter ausgegeben wird. Nach Erreichen des letzten Parameters wird beim Folgeaufruf $CMDLINEPARSE_END zurückgegeben.
Ein Aufruf könnte z.B. so aussehen:
c:\Pfad\meine.exe -f -b -c:7 --file="path to file\file.ext" argX argY
Mi diesem Bsp. werden die gesamte Parameterzeile sowie die Informationen zu jedem Parameter ausgegeben:
#include "CmdLineParser.au3"
Local $Parser = _CmdLineParser_Init()
Local $sWrite = 'FullParameterLine: ' & $Parser.cmdline & @CRLF & @CRLF
Do
$Param = _CmdLineParser_Next()
If Not @error Then
$sWrite &= 'index: ' & $Param.index & @CRLF
$sWrite &= 'key: ' & $Param.key & @CRLF
$sWrite &= 'val: ' & $Param.val & @CRLF
$sWrite &= 'kind: ' & $Param.kind & @CRLF
$sWrite &= 'param: ' & $Param.param & @CRLF
$sWrite &= 'pos: ' & $Param.pos & @CRLF & @CRLF
EndIf
Until $Param = $CMDLINEPARSE_END
ConsoleWrite($sWrite & @CRLF)
Alles anzeigen
Übrigens, wenn ihr das aus SciTE heraus testet (mit Shift+F8 Parameter setzen) bekommt ihr 3 zusätzliche Parameter: /stdin /stdout "Programmaufrufzeile", die in kompilierten Skripten natürlich nicht enthalten sind.
CommandLineParser
;-- TIME_STAMP 2020-05-23 22:45:34 v 0.1
; ============================================================ ;
; ;
; Inspired by the Nim command line parser ;
; https://nim-lang.github.io/Nim/parseopt.html ;
; ;
; ============================================================ ;
#cs CMD-LINE RULES
Basics: • Parameters will delimited by one space. (key=val key2=val key3=val)
• Values with spaces inside has to be encapsulated with string delimiters.
(key="val with space")
• For the value assignment, you can use "=" or ":". (key=val | key:val)
• Only for assignment with ":" its allowed to have one optional space before the value.
(key:val | key: val)
Types: CMD_SHORTOPTION
• Starts with "-" (-k=val)
• Name (the key) has a length of only 1 character!!
If a longer key will used, it will ignored with one exception: If the passed value is a string with
space(s) inside, the part behind the 1st space will recognized as CMD_ARGUMENT, which are summarized
by the following contiguous non-space characters.
• [SPECIAL CASE] "-key"
A short option parameter without assignment can be longer than one character.
CMD_LONGOPTION
• Starts with "--" (--key=val)
• Identifier has a length of minimum 1 character.
• [SPECIAL CASE] "--" alone
The "--" option, commonly used to denote that every token that follows is an argument,
is interpreted as a long option, and its name is the empty string.
CMD_ARGUMENT
• All other non-space charcters (arg | /abc)
Options: Parameters can declared as "ParamNoVal". This means that only a key is expected.
Since this is a parameter with no value, it is always a Boolean parameter (passed or not).
If a value is passed anyway, it is ignored.
To declare the parameter as "ParamNoVal", you initialize the parser with a list of
this paramter(s). It's one list for ShortNoVal and another one for LongNoVal.
FUNCTIONS:
_CmdLineParser_Init()
Initializes the parser. Reads the command line and creates a separate structure for each parameter.
_CmdLineParser_Next()
Returnes the next parameter structure from stack.
_CmdLineParser_GetParamByKey()
Gets a parameter structure by its key
_CmdLineParser_GetParamByIndex()
Gets a parameter structure by its index (0-based)
#ce
; CmdLineKind:
Global Const $CMDLINEPARSE_END = 0xDEADBEEF
Global Const $CMDLINEPARSE_ARGUMENT = 1
Global Const $CMDLINEPARSE_LONGOPTION = 2
Global Const $CMDLINEPARSE_SHORTOPTION = 3
Global $_ga_tCmdLineParam[1]
Global $_gt_OptParser
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_Init
; Description ...: Initializes the parser. Reads the command line and creates a separate structure for each parameter.
; ...............: Parameter structure:
; ...............: .index (0-based index of the parameter)
; ...............: .pos (start position of the parameter in the command line)
; ...............: .kind ($CMDLINEPARSE_ARGUMENT | $CMDLINEPARSE_LONGOPTION | $CMDLINEPARSE_SHORTOPTION)
; ...............: .param (the full param, i.e.: --file="my file path")
; ...............: .key (the key, i.e.: file)
; ...............: .val (the value, i.e.: "my file path")
; Parameters ....: $_sShortNoVal [optional] Comma separated list with short parameters, which have no values, i.e.: "f,c". (Default = "")
; ...............: $_sLongNoVal [optional] Comma separated list with long parameters, which have no values, i.e.: "corner,angle". (Default = "")
; Return values .: The Parser structure:
; ...............: .cmdline The full command line
; ...............: .cmdcount The count of parameters in command line
; Author ........: BugFix
; ===============================================================================================================================
Func _CmdLineParser_Init($_sShortNoVal="", $_sLongNoVal="")
Local $cmdLine = $CmdLineRaw
If $cmdLine = '' Then Return SetError(1,0,0)
Local $aTmp, $aCmds, $iCmdCount
Local $aListShortNoVal = ObjCreate("System.Collections.ArrayList")
Local $aListLongNoVal = ObjCreate("System.Collections.ArrayList")
If $_sShortNoVal <> '' Then
$aTmp = StringSplit($_sShortNoVal, ',')
For $i = 1 To $aTmp[0]
$aListShortNoVal.Add($aTmp[$i])
Next
EndIf
If $_sLongNoVal <> '' Then
$aTmp = StringSplit($_sLongNoVal, ',')
For $i = 1 To $aTmp[0]
$aListLongNoVal.Add($aTmp[$i])
Next
EndIf
$aCmds = __GetParamArray($cmdLine, $aListShortNoVal, $aListLongNoVal) ; [[sParam(--key=val), iKind, iPos, Key, Val]]
$iCmdCount = UBound($aCmds)
ReDim $_ga_tCmdLineParam[$iCmdCount]
; create parameter Dll-Struct Item
For $i = 0 To $iCmdCount -1 ; $Index, $_pos, $_kind, $_key, $_val, $_param
$_ga_tCmdLineParam[$i] = __tParam_Create($i, $aCmds[$i][2], $aCmds[$i][1], $aCmds[$i][3], $aCmds[$i][4], $aCmds[$i][0])
Next
; create parser Dll-Struct
$_gt_OptParser = __tOptParser_Create($cmdLine, $iCmdCount)
Return $_gt_OptParser
EndFunc ;==>_CmdLineParser_Init
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_Next
; Description ...: Returnes the next parameter structure from stack.
; Return values .: Success Structure of next parameter
; ...............: Failure $CMDLINEPARSE_END set @error = 1, end of stack is reached.
; Author ........: BugFix
; Remarks .......: If $CMDLINEPARSE_END was returned, with next call of the function it starts again with the first parameter.
; ===============================================================================================================================
Func _CmdLineParser_Next()
Local Static $bIter = False
Local Static $Iter
If Not $bIter Then
$Iter = __IteratorNum(0, UBound($_ga_tCmdLineParam) -1, 1)
$bIter = True
EndIf
Local $iCurrentCmd = $Iter()
If $iCurrentCmd = Null Then
$bIter = False
Return SetError(1, 0, $CMDLINEPARSE_END)
Else
Return $_ga_tCmdLineParam[$iCurrentCmd]
EndIf
EndFunc ;==>_CmdLineParser_Next
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_GetParamByKey
; Description ...: Gets a parameter structure by its key
; Parameters ....: $_sKey The searched key
; Return values .: Success The parameter structure
; ...............: Failure 0 set @error = 1, key not found
; Author ........: BugFix
; ===============================================================================================================================
Func _CmdLineParser_GetParamByKey($_sKey)
Local $t, $iCount = $_gt_OptParser.cmdcount
If $iCount > 0 Then
For $i = 1 To $iCount
$t = $_ga_tCmdLineParam[$i-1]
If $t.key = $_sKey Then Return $t
Next
EndIf
Return SetError(1,0,0)
EndFunc ;==>_CmdLineParser_FindOpt
; #FUNCTION# ====================================================================================================================
; Name ..........: _CmdLineParser_GetParamByIndex
; Description ...: Gets a parameter structure by its index (0-based)
; Parameters ....: $_iIndex The index of the searched parameter
; Return values .: Success The parameter structure
; ...............: Failure 0 set @error = 1, Index out of range
; Author ........: BugFix
; ===============================================================================================================================
Func _CmdLineParser_GetParamByIndex($_iIndex)
If $_iIndex < 0 Or $_iIndex > $_gt_OptParser.cmdcount -1 Then
Return SetError(1,0,0)
Else
Return $_ga_tCmdLineParam[$_iIndex]
EndIf
EndFunc ;==>_CmdLineParser_FindOpt
#Region - helper functions
; creates the parameter structure
Func __tParam_Create($_index, $_pos, $_kind, $_key, $_val, $_param)
Local $iLenK = StringLen($_key) +1, $iLenV = StringLen($_val) +1, $iLenP = StringLen($_param) +1
Local $tag = "struct;int pos;int index;int kind;wchar key[" & $iLenK & "];wchar val[" & $iLenV & "];wchar param[" & $iLenP & "];endstruct;"
Local $t = DllStructCreate($tag)
$t.index = $_index
$t.pos = $_pos
$t.kind = $_kind
If $_key <> '' Then $t.key = $_key
If $_val <> '' Then $t.val = $_val
$t.param = $_param
Return $t
EndFunc ;==>__tParam_Create
; creates the parser structure
Func __tOptParser_Create($_cmdLine, $_countParam)
Local $tag = "struct;" & _
"wchar cmdline[" & StringLen($CmdLineRaw) +1 & "];" & _
"int cmdcount;" & _
"endstruct;"
Local $t = DllStructCreate($tag)
$t.cmdline = $_cmdLine
$t.cmdcount = $_countParam
Return $t
EndFunc ;==>__tOptParser_Create
; selects all parameter from command line
Func __GetParamArray($_sLine, $_aListShortNoVal, $_aListLongNoVal)
Local $sPatternParam = _
'(?x)(?(DEFINE)' & _
' (?<String> "(?> [^"]+ | "" )*"' & _ ; double quoted string
' |' & _
" '(?> [^']+ | '' )*" & _ ; single quoted string"'
' )' & _
' (?<StartOrSpace> (?<= ^|\s)' & _ ; start of the line or after a space
' )' & _
' (?<SpaceOrEnd> (?= \s|$)' & _ ; before a space or line end
' )' & _
' (?<ArgumentsOnlyMarker> (?&StartOrSpace) -- (?= \s)' & _ ; all following params are read as argument
' )' & _
' (?<ShortParamNoVal> (?<!-) (?&StartOrSpace) - [^-\s:=]+ (?&SpaceOrEnd)' & _ ; short param w.o. value
' )' & _
' (?<LongParamNoVal> (?&StartOrSpace) -- [^-\s:=]+' & _ ; long param w.o. value
' )' & _
' (?<ShortParam> (?<!-) (?&StartOrSpace) - [^-\s:=] (: \s? | =) ((?&String) | \S+ )' & _ ; short param w. value
' )' & _
' (?<LongParam> (?&LongParamNoVal) (: \s? | =) ((?&String) | \S+ )' & _ ; long param w. value
' )' & _
' (?<Argument> (?&StartOrSpace) [^-]\S* (?&SpaceOrEnd)' & _ ; all other none-space
' )' & _
' (?<AllParam> (?&ShortParam) | (?&LongParam) | (?&ShortParamNoVal) |' & _
' (?&LongParamNoVal)(?&SpaceOrEnd) | (?&Argument) | (?&ArgumentsOnlyMarker)' & _
' )' & _
')' & _
'(?&AllParam)'
Local $aRes = StringRegExp($_sLine, $sPatternParam, 3)
Local $aParam[UBound($aRes)][5] ; [[sParam, iKind, iPos, Key, Val]]
Local $bArgsFollows = False, $aMatch
Local $iPosSum = 0
For $i = 0 To UBound($aParam) -1
$aParam[$i][0] = $aRes[$i]
$aParam[$i][1] = __GetParamType($aRes[$i])
$aParam[$i][2] = $iPosSum +1
$iPosSum += StringLen($aParam[$i][0]) +1 ; +1 following space
Switch $aParam[$i][1]
Case $CMDLINEPARSE_ARGUMENT
$aParam[$i][3] = $aParam[$i][0]
Case $CMDLINEPARSE_LONGOPTION
If $bArgsFollows Then
$aParam[$i][1] = $CMDLINEPARSE_ARGUMENT
$aParam[$i][3] = $aParam[$i][0]
Else
If $aParam[$i][0] = '--' Then
$bArgsFollows = True
Else
$aMatch = StringRegExp($aParam[$i][0], '--(\w+)(?:=|:\s?)?(.*)', 3)
If IsArray($aMatch) Then
$aParam[$i][3] = $aMatch[0]
If Not $_aListLongNoVal.Contains($aMatch[0]) Then $aParam[$i][4] = $aMatch[1]
EndIf
EndIf
EndIf
Case $CMDLINEPARSE_SHORTOPTION
If $bArgsFollows Then
$aParam[$i][1] = $CMDLINEPARSE_ARGUMENT
$aParam[$i][3] = $aParam[$i][0]
Else
$aMatch = StringRegExp($aParam[$i][0], '^-(?:(\w+$|\w)(?:=|:\s?)?(.*))', 3)
If IsArray($aMatch) Then
$aParam[$i][3] = $aMatch[0]
If Not $_aListShortNoVal.Contains($aMatch[0]) Then $aParam[$i][4] = $aMatch[1]
EndIf
EndIf
EndSwitch
;== DEBUG ==
;~ ConsoleWrite('> Param full: ' & $aParam[$i][0] & @TAB & ' Type(1-arg/2-long/3-short): ' & $aParam[$i][1] & @CRLF)
;~ ConsoleWrite('- Pos: ' & $aParam[$i][2] & @TAB & ' Key: ' & $aParam[$i][3] & @TAB & ' Val: ' & $aParam[$i][4] & @CRLF)
;== /DEBUG ==
Next
Return $aParam
EndFunc ;==>__GetParamArray
; detects the kind of a param, marked with "-", "--" or nothing
Func __GetParamType($_param, $_bLiteral=False)
Local $aConstLiteral[] = ['', 'CMD_ARGUMENT', 'CMD_LONGOPTION', 'CMD_SHORTOPTION']
If StringRegExp($_param, '^--') Then Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_LONGOPTION] : $CMDLINEPARSE_LONGOPTION)
If StringRegExp($_param, '^-') Then Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_SHORTOPTION] : $CMDLINEPARSE_SHORTOPTION)
Return ($_bLiteral ? $aConstLiteral[$CMDLINEPARSE_ARGUMENT] : $CMDLINEPARSE_ARGUMENT)
EndFunc ;==>__GetParamType
; returnes by each call the next number from a given range
Func __IteratorNum($_start=Null, $_end=Null, $_create=0)
Local Static $a[1], $index
If $_create Then
$index = -1
Local $n = 0
ReDim $a[$_end +1 -($_start)]
For $i = $_start To $_end
$a[$n] = $i
$n += 1
Next
Return __IteratorNum
Else
$index += 1
Return $index < UBound($a) ? $a[$index] : Null
EndIf
EndFunc ;==>__IteratorNum
#EndRegion
Alles anzeigen