This is a tiny getopt implementation that closely mirrors Unix (POSIX) getopt(3).
Design goals were to match the behavior of its C namesake with very little code. The former makes it familiar, compatible, and flexible. The latter makes it fine to copy and paste into stand-alone scripts.
Non-goals: built-in help, long options, options after the first non-option, optional option arguments.
Existing solutions did not meet my chosen goals:
Usage example:
set u {[-frv] [-C path] [-p pattern] [file ...]} while {[getopt argv {C:p:frv} opt arg]} { if {$opt eq "?"} { puts stderr "usage: [file tail $::argv0] $u" exit 1 } set opts(-$opt) $arg }
Remarks:
Implementation:
proc getopt {argvvar optstring optvar argvar} { upvar $argvvar argv $optvar opt $argvar arg set arg [lindex $argv 0] ;# "" if argv empty set opt [string index $arg 1] ;# "" if arg empty or length 1 if {$opt eq "" || [string index $arg 0] ne "-"} { return 0 ;# no option } set argv [lreplace $argv 0 0] ;# tentative consumption if {$arg eq "--"} { return 0 ;# terminate option processing } # consume option character set arg [string replace $arg 0 1] ;# cut off the dash, too if {$arg ne ""} { ;# not yet exhausted? set argv [linsert $argv 0 -$arg] ;# put it back for now } # check if option is valid set idx [string first $opt $optstring] if {$idx == -1 || $opt eq ":"} { puts stderr "[file tail $::argv0]: unknown option -$opt" set opt "?" return 1 ;# can call again for more } # handle option arguments if {[string index $optstring [incr idx]] eq ":"} { if {$arg eq ""} { if {[llength $argv] == 0} { puts stderr "[file tail $::argv0]: -$opt requires an argument" set opt [expr {[string index $optstring 0] eq ":" ? ":" : "?"}] } set arg [lindex $argv 0] ;# use next arg, no-op if empty } set argv [lreplace $argv 0 0] ;# consume arg, no-op if empty } else { set arg 1 ;# boolean flag } return 1 }