Version 47 of command options

Updated 2012-11-30 19:43:36 by pooryorick

"command options" suggests several distinct topics:

  • tclsh and wish arguments
  • command-line options (as seen through $::argv) of Tcl-coded applications
  • idioms for parsing variable (args) arguments seen by pure-Tcl procs.

Certain considerations are common to all these, and are convenient to treat here in a unified way.


  • cmdline in tcllib. This is probably the most standard and widely-used of these packages.
  • Larry Smith contributes init, which parses your command's arguments as well as initializing local variables.
  • Tcl_ParseArgsObjv allows parsing of C implemented Tcl commands using the object interface. (See example below.)
  • RS's lightweight getopt
  • getopts [L1 ] [mildly buggy]
  • clig (command line interpreter generator) [L2 ] (working links to src)
  • Extral's args_parse, cmd_parse, and so on
  • super getopts [L3 ]
  • optcl
  • Laurent Demailly's opt
  • aqtools includes an ... [?] package for argument parsing
  • yaap
  • GenParseCmdLine
  • ::tcl::OptProc is shipped with Tcl, but deprecated.
  • optparse is in 0.4 of tcllib. It's also deprecated, in favor of cmdline. [*Is* it in tcllib0.4? In any case, it's deprecated ...]
  • argp (optional argument parser) parses commandline arguments and optional arguments of procs [L4 ]
  • ad_proc [L5 ] from the ACS [L6 ]
  • Doug Simpson posted [L7 ] his "groom" to comp.lang.tcl.
  • There is argument processing code at http://jazimmer.com/tclbook/tcl/examples/html/zproc_usage.html by jazimmer .
  • Michael Kraus has code in http://ourworld.compuserve.com/homepages/mmg_kraus/mkGenMan.htm for processing arguments to procedures.
  • The Simple Development Library [L8 ] includes both a package for command arguments handling including typed arguments (with optional tun-time checking) and options (SimpleProc, [L9 ]) and a very powerful command line options parser with too many features to list here (SimpleOption, [L10 ]).
  • SNTL at http://www.csua.berkeley.edu/%7Esls/woa/distrib/ contains code for command line argument processing as well as many other items. It was written by Sam Shen.
  • YAAP is Yet Another Argument Parsing utility and can be found at ftp://ftp.procplace.com/pub/tcl/sorted/packages-7.6/devel/yaap-0.9.shar.gz . It is a template based argument parsing utility inspired by XtGetApplicationResources() .
  • evaluate_parameters at ftp://ftp.Lehigh.EDU/pub/evap/evap-2.x/evap-2.2.tar.Z is a Perl program that processes command line arguments in a simple consistent manner performing type-checking, multi-levels of help, etc. a Tcl/Tk GUI wrapper around one's Perl or C program to gather the command line arguments interactively.
  • TWAPI contains the parseargs [L11 ] command for flexible argument parsing. This is implemented in C and is therefore fast enough to be used for argument parsing in any proc. Some of the Tcl based parsers are slow enough that they impose a significant performance penalty if used for general proc arg processing as opposed to just dealing with command line options where performance of course does not matter.
  • For another simple command line parsing routine in a few lines of tcl (included help formatting), see Matthias Hoffmann - Tcl-Code-Snippets - misc routines - command line parsing
  • [dict with $args {}] provides extremely simple parsing.
  • optionscheck, in Simple way to parse command options, is a single Tcl 8.5 command created by Fabricio Rocha which verifies the formatting of a traditional options list like "-option1 value1 -option2 value2 ...". Intended for being used in megawidgets and customized commands, the procedure finds unknown options, values without preceding options, options without a value assigned to them. It returns a dict containing all those cases above along with the valid option/value pairs, and can generate errors in such cases or work silently.
  • option parsing using ensemble

Question: In C, for Tcl, using the newer objc, objv API, how do I parse options? I would have thought people would need to do this all the time, but I can't find anything. Needs to handle args, options, required optional etc. Speed is high on the priority list...

If you can use the Tk library, try Tk_SetOptions and its assorted support functions. It would be nice if something like this were in the Tcl library, for those times when one doesn't have Tk around. A pure-Tcl solution might be to do the option handling in Tcl, passing a fixed number of arguments to the C code, but that might not meet your speed requirements.

SPB I've modified the Tk_ParseArgv() routine in tkArg.hc to handle this case. Also, to make it even more useful, I added a Tcl_ParseArgsObjv for parsing a bunch of arguments passed to a C-implemented Tcl command. For example, you can do something like:

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <tcl.h>
 #include <tclArgv.h>

 int mycoolcommand(ClientData clientData,  Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
 {
   int mytoggle=0;
   int myint=-2; 

   Tcl_ArgvInfo argTable[] = {
     {"-toggle",TCL_ARGV_CONSTANT,(void*)1,&mytoggle,"turn toggle on"},
     {"-theint",TCL_ARGV_INT,NULL,&myint,"set myint"},
     {(char*)NULL,TCL_ARGV_END,NULL,NULL,(char*)NULL}
    }; 

   Tcl_Obj **private_objv=(Tcl_Obj**)calloc(objc+1,sizeof(Tcl_Obj*));
   int i;
   for (i=0;i<objc;i++) private_objv[i]=objv[i];

    if (Tcl_ParseArgsObjv(interp,&objc,private_objv,argTable,0)!=TCL_OK) return TCL_ERROR; 

    /* after this call, private_objv only holds unprocessed arguments, and objc reflects this. */ 

    ... do stuff...
   return TCL_OK;
 }

If you feel this may be of use, you can grab the tar file from http://www.sambromley.com/tclArgv/

Sam.

}


[Many people write their own "... -arg1 val1 -arg2 val2 ..." processing, because it's so easy to use Tcl associative arrays (see "Arrays / Hash Maps") simply as the "optional arguments" section in Tcl Gems does ...]


Here is the start of some code to show at least one method of doing command line parsing. Hopefully people will contribute other samples as appropriate.

 # If this script was executed, and not just "source"'d, handle argv
 if { [string compare [info script] $argv0] == 0} {
      while {[llength $argv] > 0 } { 
         set flag [lindex $argv 0]
         switch -- $flag {
          "-bool" {
                  set bool 1
                  set argv [lrange $argv 1 end]
                  }
          "-option" {
                  set value [lindex $argv 1]
                  set argv [lrange $argv 2 end]
                   }
           default { break }
      }
    }
 }

 foreach file $argv {
    puts "[format "file: %s" $file]"
 }

PT writes: I like to use code similar to the above example for option processing. However, we want to avoid 'shimmering' the args list into a string and back into a list. Plus I find it repatative to keep writing the same two lines for setting the option value. Here is my current example:

 # -------------------------------------------------------------------------
 # Description:
 #  Pop the nth element off a list. Used in options processing.
 #
 proc dns::Pop {varname {nth 0}} {
     upvar $varname args
     set r [lindex $args $nth]
     set args [lreplace $args $nth $nth]
     return $r
 }

 # ---------------------------------------------------------------------
 # Now the option processing loop. Eats the arguments once handled and stops at '--'

 # setup the defaults.
 array set opts {-a 1 -b 0}

 while {[string match -* [lindex $args 0]]} {
     switch -glob -- [lindex $args 0] {
         -A*
         -a*   { set opts(-a) [Pop args 1] }
         -b*   { set opts(-b) [Pop args 1] }
         --    { Pop args ; break }
         default {
             set opts [join [lsort [array names state -*]] ", "]
             return -code error "bad option [lindex $args 0]: \
                     must be one of $opts"
         }
     }
     Pop args
 }

 puts "Options now [array get opts] and the remaining args: $args"

I use procargs to do this job JBR


A quick and dirty way uses one-liners like this:

 if [regexp " -x" $::argv] {# do the X thing} ;# RS

If your values come in pairs (like -option value), I usually use the following code for parsing them:

 foreach {option value} $argument_list {
   switch -glob -- $option {
     -opt*      {set opt $value}
     -otheropt* {set otheropt $value}
     default    {error "Unknown option $option!"}
   }
 }

A classical and elegant solution to this task is from the Welch book:

 proc foo {args} {
    # first assign default values...
    array set options {-bar 1 -grill 2 -verbose 0 ...}
    # ...then possibly override them with user choices
    array set options $args
    ....
 }

It does not check for undefined switches, though. (RS)


troym72, 2009-12-22: For passing arguments to a Tcl script from the UNIX command line in the form of a keyed list, it can be a little tricky. Here's an example proc that I created to help illustrate how Tcl handles arguments from the command line in keyed list form.

#!/usr/bin/tcl

#  Script Name: argstest.tcl
# Date Created: 12/22/2009
#       Author: Troy Morton
#      Purpose: Test command line arguments

package require tclx

proc parseargs {args} {
  
  echo $args                   ;# args as passed from the “parseargs $argv procedure call”
  set args [lindex $args 0]    ;# pull my args keyed list from the argv single element list
  keylget args KEY1 value      ;# now do my keylget from the args which is now a properly formatted keyed list.
  echo $value                  ;# echo value of the key “KEY1”.
  
}

echo $argv                   ;# args from command line arrive as keyed list
parseargs $argv              ;# argv is passed to pareargs as a list

When run from the UNIX command line the above script looks like this:

/>argstest.tcl "KEY ValueKey1" "KEY2 ValueKey2"     ;# typed on command line
{KEY1 ValueKey1} {KEY2 ValueKey2}                   ;# argv as received by the tcl script
{{KEY1 ValueKey1} {KEY2 ValueKey2}}                 ;# argv as received by proc parseargs
ValueKey1                                           ;# value of the key KEY1

SB, 2003-05-21: For many of my tcl utilities I have only vanilla tclsh but still need to parse command line options. As I mostly use the tools myself I don't need too much fancy error detection. For a small application, I need a couple of switches, one with an argument and then the file to process. Nothing fancy, but it is written in a matter of seconds, and it is fairly easy to extend with more switches.

 set arglen [llength $argv]
 set index 0
 while {$index < $arglen} {
   set arg [lindex $argv $index]
   switch -exact -- $arg {
       {-s} {set args($arg) [lindex $argv [incr index]]}
       {-l} {set args($arg) .}
       default  {set filename [lindex $argv $index]}
   }
   incr index
 }
 if {[info exists args(-l)]} {
    puts "-l switch set"
    ... code what happens for -l
 }
 if {[info exists args(-s)]} {
    puts "-s switch set with arg $args(-s)"
    ... code what happens for -s and its arg
 }

A related topic: "Syntax parsing in Tcl".


Tcl syntax - Arts and crafts of Tcl-Tk programming