Tcl Part I · Module 5 · Strings
Glob Matching: string match
Very often the question is not where a piece of text sits but whether a string has a particular shape — does this file name end in .log, does this identifier start with a prefix, is this one of a small family of names. Tcl answers that with glob patterns, which have exactly three pieces of syntax and fit entirely in your head. They are the right tool for most of these questions and they are far easier to get right than a regular expression, which is why this chapter comes long before the module that teaches those.
Foundation12 min readTclPart IStringsMatchingPatterns
Part I · Module 5 · Chapter 5.5 · Glob Matching: string match
1. The Command
puts [string match "*_tb" "fifo_tb"]
puts [string match "*_tb" "fifo"]
puts [string match "*.txt" "report.txt"]1
0
1It returns 1 or 0 — a truth value you can put straight in a condition.
The pattern comes first, which is the same ordering oddity as string first and is flagged in Chapter 5.1 §5.
2. The Whole Syntax
Three wildcards. That is all of it:
| Pattern | Meaning |
|---|---|
* | any run of characters, including none |
? | exactly one character |
[abc] | one character from the set |
puts "star: [string match "rep*" "report"]"
puts "quest: [string match "???" "abc"]"
puts "quest4: [string match "???" "abcd"]"
puts "set: [string match {[abc]*} "banana"]"
puts "set-no: [string match {[abc]*} "zebra"]"star: 1
quest: 1
quest4: 0
set: 1
set-no: 0A range works inside the brackets too — [a-z], [0-9].
Compare that table with a regular expression reference and you can see the argument for this chapter. Three symbols, and you are done.
3. The Rule That Catches People
The pattern must match the entire string, not a part of it:
puts "error -> [string match "error" "error: failed"]"
puts "error* -> [string match "error*" "error: failed"]"
puts "*error* -> [string match "*error*" "an error occurred"]"error -> 0
error* -> 1
*error* -> 1The first is 0 even though the text plainly begins with error. As a pattern, error means exactly those five characters and nothing else — and the string has more.
Add a trailing * and it matches. If you want "contains", say so with stars on both sides.
This is the opposite of grep and of regular expressions, which match anywhere by default. Anyone arriving from those tools writes the pattern without stars once and then wonders why nothing matches.
4. Ignoring Case
puts [string match -nocase "error*" "ERROR: failed"]
puts [string match "error*" "ERROR: failed"]1
0-nocase goes before the pattern. Without it, matching is case-sensitive.
For log scanning, where severity words arrive in whatever case the tool felt like, -nocase is usually what you want.
5. Matching a Literal Wildcard
To match an actual * or ?, escape it:
puts "escaped star matches a real star: [string match {a\*b} {a*b}]"
puts "unescaped star matches anything: [string match {a*b} {axxxb}]"escaped star matches a real star: 1
unescaped star matches anything: 1The first pattern means "a, then a literal asterisk, then b". The second means "a, anything, b".
This comes up with shell-glob-looking data and with file names containing brackets.
6. Where You Will Use It
Classifying a name:
foreach f {report.txt data.csv notes.txt archive.tar.gz} {
if {[string match "*.txt" $f]} {
puts "$f -> text"
} elseif {[string match "*.csv" $f]} {
puts "$f -> spreadsheet"
} else {
puts "$f -> other"
}
}report.txt -> text
data.csv -> spreadsheet
notes.txt -> text
archive.tar.gz -> otherFiltering lines:
set lines {"INFO starting" "ERROR disk full" "INFO done" "WARN slow"}
foreach line $lines {
if {[string match -nocase "error*" $line]} {
puts "found: $line"
}
}found: ERROR disk fullValidating a shape:
foreach id {AB-1234 XY-99 ab-1234 A-1234} {
puts "$id -> [string match {[A-Z][A-Z]-[0-9][0-9][0-9][0-9]} $id]"
}AB-1234 -> 1
XY-99 -> 0
ab-1234 -> 0
A-1234 -> 0Two uppercase letters, a dash, four digits. That pattern is readable; the equivalent regular expression is not much longer but is considerably easier to get wrong.
7. When a Glob Is Not Enough
Globs have no way to express:
- "one or more" or "between two and four" repetitions
- alternatives of more than one character —
errororfatal - capturing part of the match for later use
- anchoring to anything other than the whole string
When you need any of those, you need a regular expression, and Module 13 teaches them — opening with the same advice this chapter gives, in reverse: do not reach for a regex when a glob would do.
A useful middle step: several string match tests joined with || cover the "one of these" case perfectly well.
set line "FATAL cannot continue"
set bad [expr {[string match -nocase "error*" $line] ||
[string match -nocase "fatal*" $line]}]
puts "is a failure line: $bad"is a failure line: 18. Related, and Not the Same
Two neighbours worth separating in your mind:
glob(Module 12) uses the same patterns to list files on disk. Same syntax, different job.switch -glob(Module 8) matches one value against several patterns in a table, which is much tidier than a chain ofifs once you have more than two.lsearch -glob(Module 6) finds list elements matching a pattern.
The pattern syntax in this chapter is the same in all three.
9. Common Mistakes
Forgetting the pattern must match the whole string. §3. Add * where you meant "contains".
Not bracing a pattern containing [. §2. Tcl tries to run a command.
Expecting case-insensitivity by default. Use -nocase. §4.
Argument order. Pattern first.
Reaching for a regular expression first. For shape questions, a glob is clearer and shorter.
Using string match to extract part of a string. It only answers yes or no. To pull something out you need positions (Chapter 5.4) or a regular expression.
10. Exercises
Exercise 1 — predict nine. For the string "report_2024.log", predict the result of each pattern, then check: *.log, report*, *2024*, report, ??????_2024.log, *.txt, *_????.log, [rs]*, *LOG.
Exercise 2 — contains. Write a pattern that is true when a line contains timeout anywhere. Test it against three lines where it should match and one where it should not.
Exercise 3 — brace it. Write a pattern matching any name that starts with a digit. Try it both braced and quoted, and explain the difference.
Exercise 4 — a shape. Write a pattern for an identifier of exactly three uppercase letters followed by two digits. Test it with ABC12, AB12, abc12 and ABC123.
Exercise 5 — literal wildcard. Write a test that is true only for the exact three characters a*b. Confirm it rejects axxb.
Exercise 6 — several alternatives. Classify a line as a failure when it starts with error, fatal or panic, ignoring case. Then say how you would write this once there were ten such words.
11. Remember
string match pattern stringreturns 1 or 0. Pattern first.- Three wildcards:
*any run,?one character,[abc]one from a set, with ranges like[a-z]. - The pattern must match the whole string. Use
*on both sides for "contains". - Matching is case-sensitive unless you pass
-nocase. - Brace patterns, especially any containing
[. - Escape a literal wildcard as
\*or\?. - It answers yes or no only — it cannot extract.
- Prefer a glob to a regular expression for shape questions. Module 13 covers the cases globs cannot express.
- The same patterns work in
glob,switch -globandlsearch -glob.
Next: comparing two strings at command level.
Where this fits
Part of the Tcl curriculum.
