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} ;# RSIf 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 listWhen 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 KEY1PYK 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 $argvtarzan - 2017-05-04 08:31:38
TclArgv on github