Skip to content
VLSI Mentor

Tcl Part I · Module 8 · Conditions and Control Flow

switch Matching Modes: -exact, -glob, -regexp

By default switch compares its value to each pattern character for character, and that is the right behaviour most of the time. Two options change it: one turns the patterns into the glob patterns you already learned for string match, and one turns them into regular expressions. This chapter covers all three, plus the separator that appears in every switch in this module without having been explained. That separator is usually taught as a superstition — always write it, nobody says why. There is a real answer, it is more specific than the folklore, and it is worth two minutes.

Foundation11 min readTclPart IControl flowswitchPatterns

Part I · Module 8 · Chapter 8.5 · switch Matching Modes: -exact, -glob, -regexp

1. -exact is the default

Without an option, patterns are compared character for character:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
switch -- "fa" {
    fa* { puts "matched the glob" }
    fa  { puts "matched exactly" }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
matched exactly

The pattern fa* did not match the value fa, because in exact mode * is just an asterisk. Only the literal fa matched.

Writing -exact explicitly is legal and says what you mean:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
switch -exact -- "fa" {
    fa* { puts "glob" }
    fa  { puts "exact" }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
exact

2. -glob

-glob turns the patterns into the glob language from Chapter 5.5 — * for any run of characters, ? for exactly one, [...] for a character set:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach file {report.log data.csv notes.txt archive.tar.gz} {
    set kind [switch -glob -- $file {
        *.log    { expr {"log file"} }
        *.csv    { expr {"spreadsheet"} }
        *.tar.gz { expr {"archive"} }
        default  { expr {"something else"} }
    }]
    puts [format "%-16s %s" $file $kind]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
report.log       log file
data.csv         spreadsheet
notes.txt        something else
archive.tar.gz   archive

Order matters as always — the first matching pattern wins — so put the more specific patterns first when two could both match.

3. -regexp

For anything glob cannot express:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach s {width=32 depth=16 mode=fast bogus} {
    set out [switch -regexp -- $s {
        {^\w+=\d+$}   { expr {"numeric setting"} }
        {^\w+=\w+$}   { expr {"text setting"} }
        default       { expr {"not a setting"} }
    }]
    puts [format "%-12s %s" $s $out]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
width=32     numeric setting
depth=16     numeric setting
mode=fast    text setting
bogus        not a setting

Braces around each regular expression matter: they stop Tcl substituting $ and \ before the pattern reaches switch, exactly as braces do everywhere else.

-matchvar captures the parenthesised groups into a list, with the whole match first:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
switch -regexp -matchvar m -- "width=32" {
    {^(\w+)=(\d+)$} {
        puts "key '[lindex $m 1]' has value '[lindex $m 2]'"
    }
    default { puts "no match" }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
key 'width' has value '32'

4. -nocase

Works on its own and alongside the other modes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
switch -- "FAST" {
    fast    { puts "hit" }
    default { puts "miss" }
}

switch -nocase -- "FAST" {
    fast    { puts "hit" }
    default { puts "miss" }
}

switch -nocase -glob -- "REPORT.LOG" {
    *.log   { puts "a log file" }
    default { puts "miss" }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
miss
hit
a log file

5. What -- actually does

-- marks the end of the options. Everything after it is data, even if it starts with a dash.

The usual explanation stops there, which leaves you wondering why the switches in Chapter 8.4 all had one when none of their values began with a dash. Here is the specific answer.

Tcl stops looking for options once only two arguments remain — the value and the body list. So in the common braced form, a value starting with a dash is already safe:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set arg "-zzz"
switch $arg {
    -zzz    { puts "matched" }
    default { puts "default" }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
matched

No error, even though -zzz is not a real option. Now write the same thing in the multi-argument form, where more than two arguments remain:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set arg "-v"
switch $arg -v {puts "matched"} default {puts "default"}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
bad option "-v": must be -exact, -glob, -indexvar, -matchvar, -nocase, -regexp, or --

There it is. Tcl had more than two arguments left, so it tried to read -v as an option. With -- it works:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set arg "-v"
switch -- $arg -v {puts "matched"} default {puts "default"}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
matched

6. Choosing a mode

You want toUse
Compare against fixed stringsnothing (or -exact)
Match filenames, suffixes, prefixes-glob
Match structure — digits, anchors, groups-regexp
Ignore caseadd -nocase
Capture part of the match-regexp -matchvar

Default to exact. It is the fastest, it has no surprises, and a pattern containing * or . in exact mode means what it looks like rather than something cleverer.

7. A worked example

Classifying input lines with the mode chosen per job:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc classify {line} {
    # exact first: the few fixed keywords
    switch -- $line {
        BEGIN   { return "marker: begin" }
        END     { return "marker: end" }
    }
    # then structure
    switch -regexp -- $line {
        {^#}            { return "comment" }
        {^\s*$}         { return "blank" }
        {^(\w+)\s*=}    { return "assignment" }
    }
    # then shape
    switch -glob -- $line {
        *.log  { return "log filename" }
        *      { return "unrecognised" }
    }
}

foreach l {BEGIN "# a note" "width = 32" "" "run.log" "???"} {
    puts [format "%-12s %s" "'$l'" [classify $l]]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
'BEGIN'      marker: begin
'# a note'   comment
'width = 32' assignment
''           blank
'run.log'    log filename
'???'        unrecognised

Three switch commands, each in the mode that suits its question, each falling through to the next when nothing matched. Note the first two have no default — that is how they hand control onward.

8. Exercises

Exercise 1. Why does the pattern fa* fail to match the value fa in switch -- "fa" { fa* {...} }?

Exercise 2. Write a switch classifying a.txt, b.log and c into text, log and unknown.

Exercise 3. Explain in one sentence what -- does, and give the exact error you get without it in the multi-argument form.

Exercise 4. Which mode for each: (a) HTTP status codes 200/404/500; (b) filenames ending .v or .sv; (c) strings like clk_div_8 where you want the number; (d) YES/yes/Yes.

Exercise 5. Use -regexp -matchvar to pull the key and value out of timeout=300.

Exercise 6. Someone writes switch -regexp -- $f { *.log {...} } meaning to match log filenames. Why is that not the test they think it is?

Answers

1. Because the default mode is -exact, where * is simply an asterisk character. The pattern is the three literal characters f, a, *, and the value fa does not contain an asterisk.

2.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach f {a.txt b.log c} {
    puts [switch -glob -- $f {
        *.txt   { expr {"text"} }
        *.log   { expr {"log"} }
        default { expr {"unknown"} }
    }]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
text
log
unknown

3. It ends option processing, so the next argument is the value even if it begins with a dash. Without it, the multi-argument form gives bad option "-v": must be -exact, -glob, -indexvar, -matchvar, -nocase, -regexp, or --.

4. (a) exact — they are fixed strings; (b) -glob with *.v and *.sv; (c) -regexp -matchvar to capture the digits; (d) -nocase.

5.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
switch -regexp -matchvar m -- "timeout=300" {
    {^(\w+)=(\d+)$} { puts "[lindex $m 1] -> [lindex $m 2]" }
    default         { puts "no match" }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
timeout -> 300

6. In a regular expression * means "zero or more of the previous item" and . means "any character", so *.log is not "anything ending in .log" — it is a malformed or at best accidental pattern. The glob intent needs -glob, or the regexp {\.log$}.

9. Remember

  • -exact is the default. * and . in a pattern are ordinary characters unless you ask for another mode.
  • -glob gives the string match language: *, ?, [...].
  • -regexp gives regular expressions; brace each pattern so $ and \ survive.
  • -matchvar captures the whole match and its groups into a list.
  • -nocase works alone or combined with -glob and -regexp.
  • -- ends option processing. Tcl happens to stop looking for options with two arguments left, so the braced form is lenient — do not rely on it.
  • Without --, the multi-argument form gives bad option "-v": must be -exact, -glob, ..., which is also a free list of every valid option.
  • First match wins in every mode, so order specific patterns before general ones.
  • Choose the mode deliberately — a . means different things to glob and to regexp.

Where this fits

Part of the Tcl curriculum.