command options are ever-present in Tcl scripts. This page presents various approaches to processing them.
suggests several distinct topics:
Certain considerations are common to all these, and are convenient to treat here in a unified way.
The following example can be used as a template for processing arguments in the way that is fairly standard for built-in commands:
# If this script was executed, and not just "source"'d, handle argv if {[info exists argv0] && [ file dirname [file normalize [info script]/...]] eq [ file dirname [file normalize $argv0/...]]} { while {[llength $argv]} { puts [list ooo $argv] set argv [lassign $argv[set argv {}] flag] switch -glob $flag { -bool { set bool 1 } -option { set argv [lassign $argv[set argv {}] value] } -- break -* { return -code error [list {unknown option} $flag] } default { set argv [list $flag {*}$argv] 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 repetitive 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"
PYK 2016-02-04: As far as I can tell, neither the original code, nor the modified version I'm providing with this edit, cause $argv to shimmer, so I plan to delete the portion of the statement above, by PT, to that effect.
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 }
See named parameters
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.
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!"} } }
troym72 2009-12-22: Passing arguments from the UNIX command line in the form of a keyed list, can be a little tricky. Here's an example procedure 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
PYK 2015-10-20: That approach bundles a key and a value into a single argument, which can lead to quoting issues between the system shell and the Tcl interpreter. Better to treat $argv as a sequence of key-value pairs:
proc main {$argv0 $argv} { dict update $argv KEY KEY KEY2 KEY2 {} ... } main $argv0 $argv
tarzan - 2017-05-04 08:31:38
TclArgv on github