Jump to content

UDF: parsing cmd line switches


thomasl
 Share

Recommended Posts

This UDF helps with parsing complex command lines, of the sort you find in Unix command line tools. The approach I have chosen makes a couple assumptions (for instance, boolean switches can't be combined); in exchange the function that does the actual parsing is small and relatively tidy... and it allows for all sorts of command line parameters.

For details of how to use ParseCmdLine() see this example:

#include <ParseCmdLine.au3>

AutoItSetOption("MustDeclareVars",1)
AutoItSetOption("ExpandVarStrings",1)

; $args has 5 entries for 5 switches: -nodebug -w -d... -u... -t...
; each entry has 4 parts separated by a | (last part is optional): "type|switch|var|default"
; "type" is either b for a boolean or s for a string switch
; "switch" is what the user has to type on the command line
; "var" is the AU3 variable (w/o the $) which will hold the result (needs not be declared)
; "default" is the default initialiser for "var"
; "default" is optional: if it's mssing a boolean (ie "type" b) comes back false, a string (s) empty
;
; CAUTION: all "switch" strings should be unambiguous; however, a longer "switch" before
; a shorter "switch" in $args should work (ie first "nodebug" and later "n"). The order on
; the command line is irrelevant in any case: -nodebug will never be interpreted as -n"odebug"
; as long as -nodebug is defined before -n
;
Global $args[5]=[ _ ; Watch out: the initialisers for -d and -t require 'AutoItSetOption("ExpandVarStrings",1)')!
    "b|nodebug|DebugMode|True", _   ; boolean -nodebug: $DebugMode is True if switch is not specified
    "b|w|Write|False", _            ; boolean -w: $Write is False if switch is not specified
    "s|d|Dir|@SCRIPTDIR@\", _       ; string -d...: $Dir is Scriptdir if switch is not specified
    "s|u|User|St. Jacques", _       ; string -u...: $User is St. Jacques if switch is not specified
    "s|t|Time|@HOUR@:@MIN@:@SEC@" _ ; string -t...: $Time is current time if switch is not specified
]

; Another example:
; the "switch" part can also include a separator or other special characters:
; the following means -nodebug -w -dir=... -name=... -n
; using -nodebug, -name and -n in one command line is probably not a good idea (see comment above)
;Global $args[5]=["b|nodebug|DebugMode|True","b|w|Write","s|dir=|Dir|c:\test","s|name=|Name|Holy Cow!","b|n|NoFiles"]

; as $CmdLine[] is a read-only array (strange idea, that... and something
; the AU3 documentation should mention in passing), parsing has to be done with a copy
Global $cl=$CmdLine
If _ParseCmdLine($cl,$args)=0 Then
    If @Error=2 Then
        ConsoleWrite("error in $args[] while parsing: "&$args[@extended]&@CRLF)
    ElseIf @Error=3 Then
        ConsoleWrite("error in $cl[] while parsing: "&$cl[@extended]&@CRLF)
    EndIf
    Exit
EndIf
; at this point $cl[] holds only unprocessed switches and non-switch command line args

; so let's see the results after parsing:
WriteVar("$Dir")
WriteVar("$User")
WriteVar("$Time")
If $DebugMode Then ConsoleWrite("Debugging on"&@CRLF)
If $Write Then ConsoleWrite("Writing enabled"&@CRLF)
;If $NoFiles Then ConsoleWrite("No files"&@CRLF)

ConsoleWrite($cl[0]&" command line argument(s) after parsing:"&@CRLF)
For $i=1 To $cl[0]
    WriteVar("$cl["&$i&"]")
Next

Func WriteVar($v)
    ConsoleWrite($v&"=!>"&Execute($v)&"<!"&@CRLF)
EndFunc

Download the UDF: ParseCmdLine.au3

One nice feature I had working but removed is the capability to call AutoIt3 code for the initialiser. This is (or rather it would be) a powerful feature, but Execute() is not a powerful function. In fact, Execute(), as it is right now, looks more like a pretty bad joke. IMHO, it should be either removed from the language or it should be fully supported.

Anyway, give the stuff a whirl.

Link to comment
Share on other sites

  • 4 weeks later...

New version with two changes: a) the $switch parameter can be a regular expression; B) a non-processed switch in $args now produces an error condition.

Code for the UDF (file ParseCmdLine.au3):

#include-once

Func _If2($b,$t,$f)
  If $b Then Return $t
  Return $f
EndFunc

Func IsSwitch($cl,$switch)
  If StringLeft($switch,1)<>"^" Then $switch="^("&$switch&")"
  Local $r=StringRegExp($cl,$switch,1)
  If @Error=0 Then Return StringLen($r[0])
  If @Error=1 Then Return 0
  Return -1
EndFunc

;==============================================================================
;
; Name:            _ParseCmdLine(), v1.01
; Description:     parses command line switches
; Parameter(s):    $cl: a copy of $CmdLine (or another suitably initialised array)
;   $args is an array with the command line arguments
;     each entry in $args is a string with 4 parts, separated by a | : "type|switch|var|default"
;     "type" is either b for a boolean or s for a string switch
;     "switch" is what the user has to type on the command line (can be a simple regular expression)
;     "var" is the AU3 variable (w/o the $) that will hold the result (needs not be declared)
;     "default" is the default initialiser for "var"
;     "default" is optional: if it's missing a boolean (ie "type" b) comes back false, a string (s) empty
;   $switch is the switch character (default "-")
;   $cs=1 means switches are case-sensitive (default not case-sensitive)
; Requirement(s):  AutoIt
; Return Value(s): on success - returns 1, $cl is trimmed to hold only unprocessed entries
;   on failure - returns 0
;     @error is 1 if either $cl or $args is not an array
;     @error is 2 if there's an error in $args; @extended points to the entry in question
;     @error is 3 if there's an error in $cl; @extended points to the entry in question
;     @error is 4 if there's an unprocessed switch left in $cl; @extended points to the entry in question
;     @error is 5 if the regular expression in $switch is invalid
; Author(s):       thomasl
;
;==============================================================================
Func _ParseCmdLine(ByRef $cl,$args,$switch="-",$cs=0)
  Local $i,$j,$a,$v,$s,$l
  If Not IsArray($cl) And Not IsArray($args) Then Return SetError(1,0,0)
  For $i=0 To UBound($args)-1                  ; outer loop over all args
    $a=StringSplit($args[$i],"|")
    Switch $a[0]
      Case 3
        $v=_If2($a[1]="b",False,"")            ; use defaults
      Case 4
        $v=_If2($a[1]="b",$a[4]="True",$a[4])  ; use initialiser
      Case Else
        Return SetError(2,$i,0)
    EndSwitch
    Assign($a[3],$v,2)                        ; assign value in case the switch is not encountered
    If $cs=0 Then $a[2]=StringLower($a[2])
    For $j=1 To $cl[0]                        ; inner loop over command line args
      If $cl[$j]="" Then ContinueLoop
      $l=IsSwitch($cl[$j],($switch))
      If $l=-1 Then Return SetError(5,0,0)
      If $l>0 Then
        $s=StringMid($cl[$j],$l+1,StringLen($a[2]))
        If $cs=0 Then $s=StringLower($s)
        If $s==$a[2] Then                     ; and switch string
          $s=StringMid($cl[$j],$l+StringLen($a[2])+1)
          If $a[1]="b" Then                   ; boolean?
            If $s<>"" Then Return SetError(3,$j,0)
            Assign($a[3],_If2($v,False,True))
          Else                                ; string
            Assign($a[3],$s)
          EndIf
          $cl[$j]="" ; clear this entry
        EndIf
      EndIf
    Next
  Next
  $j=1
  For $i=1 To $cl[0] ; clean up $cl[]
    If $cl[$i]<>"" Then
      If IsSwitch($cl[$i],($switch))>0 Then Return SetError(4,$i,0)
      If $i>$j Then
        $cl[$j]=$cl[$i]
        $cl[$i]=""
      EndIf
      $j+=1
    EndIf
  Next
  $cl[0]=$j-1
  Return SetError(0,0,1) ; home and dry
EndFunc

Same as downloadable file: ParseCmdLine.au3

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...