Version 50 of command options

Updated 2013-11-28 18:21:46 by pooryorick

command options presents information about working with command options in Tcl.

See Also

category Argument Processing
Syntax parsing in Tcl
Tcl syntax
Argument Parsing, a discussion

Description

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.

Option Parsing for Tcl Commands

ad_proc
aqtools
includes an ... [?] package for argument parsing
argp
simple and flexible arg parsing for Tcl
clig
written in Tcl, generates interpreters for both standalone programs and Tcl commands
cmdline in tcllib
probably the most standard and widely-used of these packages
cmdr ,by AK
a framework for command-line parsing and dispatch. Presented at the Twentieth Annual Tcl/Tk Conference (2013)
[dict with $args {}]
provides extremely simple parsing.
Evaluate Parameters 2.4
for C , Perl, and Tcl
Extral's args_parse, cmd_parse, and so on
getopt ,by RS
light-weight, of course
groom: a simple getopt: calling Tcl procs with options ,Doug Simpson ,2001-09-04
init ,by Larry Smith
parses a command's arguments and also initialises local variables.
Matthias Hoffmann - Tcl-Code-Snippets - misc routines - command line parsing
another simple command line parsing routine in a few lines of tcl, with integrated help support
mkextensions ,by Michael Kraus
includes mkGeneric::options, a simple but efficient option-processing routine.
opt
deprecated
optparse ,by Laurent Demailly
deprecated
optional arguments, in Tcl Gems
illustrates the use of arrays to do simple argument parsing
Options and Arguments ,by aspect
option parsing using ensemble ,by Lars H
uses the namespace ensemble mechanism to to handle options
procargs ,by JBR
Procedure for Extracting Options and Switches ,by J Adrian Zimmer ,1988
Simple way to parse command options ,by Fabricio Rocha
provides optionscheck, 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.
SNTL ,by Sam Shen
contains code for command line argument processing as well as many other items.
TclGetOpts
mildly buggy
Tcl_ParseArgsObjv
parsing of C implemented Tcl commands using the object interface. (See example below.)
tepam, or Tcl's Enhanced Procedure Argument Manager
a component of tcllib
TWAPI
provides parseargs 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.
yaap - Yet Another Argument Parsing utility
The Simple Development Library
includes both SimpleProc , a package for command arguments handling including typed arguments, with optional run-time checking, and options SimpleOption , a very powerful command options parser with too many features to list here
yaap - Yet Another Argument Parsing utility
a template-based argument parsing utility inspired by XtGetApplicationResources()
ycl::checkargs
somewhat like tepam, but weaves documentation and code together in a Literate Programming style, providing a versatile argument specification system as well as argument validation and inter-argument validation

System Command Line

Evaluate Parameters 2.4
for C , Perl, and Tcl

Discussion

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.


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"

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
}