Control Flow in Tcl
Learn conditionals, loops, and branching in Tcl - if/elseif/else, switch, for, while, and foreach with Docker-ready examples
Control flow determines the order in which your program executes. Tcl approaches control flow in a way that surprises newcomers: there are no special control-flow keywords. Instead, if, while, for, foreach, and switch are ordinary commands—just like puts or set. They happen to take blocks of code (held in braces) as arguments.
This is a direct consequence of Tcl’s design philosophy: everything is a command. When you write if {$x > 5} { ... }, you are calling the if command with two arguments: an expression string and a script string. Understanding this makes Tcl’s quoting rules feel natural rather than arbitrary—the braces aren’t syntax, they are just a way to pass an unevaluated block to a command.
In this tutorial you’ll learn how Tcl handles conditionals (if/elseif/else), multi-way branching (switch), counted and conditional loops (for, while, foreach), and loop control (break, continue). A key idiom you’ll see throughout: conditions belong in braces so that expr can compile and evaluate them efficiently.
Conditionals with if/elseif/else
The if command evaluates an expression and runs a script block if it’s true. The condition is wrapped in braces so Tcl passes it to the expression evaluator unmolested.
Create a file named conditionals.tcl:
| |
A few things worth noting. The braces around {$score >= 90} are important: they let Tcl’s expr engine compile the expression once and substitute $score itself, which is both faster and safer than letting the interpreter substitute first. For string equality, Tcl provides the eq and ne operators, which compare values as strings regardless of how they look—use these instead of == when you mean “are these the same text.”
Multi-way branching with switch
When you need to compare one value against many possibilities, switch is cleaner than a chain of elseif branches. By default switch matches exact strings.
Create a file named switch_demo.tcl:
| |
The dash (-) after a pattern is the fall-through marker: it tells switch to use the next pattern’s body. This is how Tcl groups several cases under one action—here all five weekday names share the same block. The default pattern catches anything that didn’t match. With the -glob option, patterns become wildcard matches (* matches any characters), and -regexp enables full regular expressions.
Counted loops with for
The for command takes four arguments: an initialization script, a test expression, a “next” script, and the loop body. It mirrors C’s for loop but, again, it’s just a command.
Create a file named for_loop.tcl:
| |
The incr command increments a variable in place; incr n -2 decrements by 2. Because each of the four parts is a separate script argument, you have full freedom—the initialization and step parts can contain any commands, not just simple assignments.
Conditional loops with while
A while loop repeats as long as its condition expression stays true. Keep the condition in braces so it’s re-evaluated each pass.
Create a file named while_loop.tcl:
| |
This shows the two loop-control commands. break exits the innermost loop immediately, and continue skips to the next iteration. The while {1} idiom creates an infinite loop that you exit explicitly with break—a common pattern when the exit condition is easier to express in the middle of the body.
Iterating with foreach
Tcl’s foreach is its most idiomatic loop. Since lists are a native Tcl data type (any whitespace-separated string is a list), foreach walks through their elements directly—and it can even iterate over multiple variables at once.
Create a file named foreach_loop.tcl:
| |
The first loop is the everyday case: one variable, one list. The second form takes two loop variables in braces, so foreach pulls two elements off the list per iteration—handy for flat key/value lists. The third form pairs up multiple lists, advancing through all of them together. This flexibility is why experienced Tcl programmers reach for foreach far more often than for.
Running with Docker
Run any of the examples above using the official Tcl image—no local installation required.
| |
Swap in any filename from this tutorial as the final argument to run that script.
Expected Output
Running conditionals.tcl:
Grade: B
Everything is a string!
Entry allowed
Running switch_demo.tcl:
Wed is a weekday
Text file
Running for_loop.tcl:
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Countdown:
10
8
6
4
2
Running while_loop.tcl:
Stopped at total = 21
Odd numbers under 10:
1
3
5
7
9
Running foreach_loop.tcl:
Fruit: apple
Fruit: banana
Fruit: cherry
Pairs:
Alice is 30
Bob is 25
Carol is 41
ID 101 -> root
ID 102 -> admin
ID 103 -> guest
Key Concepts
- Control structures are commands —
if,while,for,foreach, andswitchare ordinary commands that take script blocks as arguments, not special syntax. - Brace your conditions — Always wrap test expressions in
{ }(e.g.while {$x < 10}). This lets Tcl’sexprengine compile and evaluate them efficiently and avoids substitution surprises. - Use
eq/nefor strings — Compare text with theeqandneoperators (orstring compare); reserve==and!=for numeric comparisons. switchfall-through uses-— A dash after a pattern shares the next pattern’s body, grouping multiple cases under one action. Add-globor-regexpfor wildcard or regex matching.foreachis the idiomatic loop — It iterates lists directly, supports multiple loop variables per step, and can walk several lists in parallel—usually clearer than an index-basedfor.breakandcontinuecontrol loops —breakexits the innermost loop;continuejumps to the next iteration. Pair them withwhile {1}for mid-body exits.incrmutates in place —incr iadds 1;incr i -2subtracts 2;incr total $iadds an arbitrary amount.
Running Today
All examples can be run using Docker:
docker pull efrecon/tcl:latest
Comments
Loading comments...
Leave a Comment