Tcl Part I · Module 3 · Values, Variables, and Expressions
unset, and Testing Whether a Variable Exists
Chapter 3.2 ended on an error: reading a variable that does not exist stops the script. That is good behaviour, because it catches typos, but it is not always what you want. Sometimes a value is genuinely optional and you need to supply a default. This chapter gives you the two commands that turn that error into a decision you control, plus the small surprise that removing a variable which is not there is itself an error unless you say otherwise.
Foundation11 min readTclPart IValuesVariablesDefensive Checks
Part I · Module 3 · Chapter 3.4 · unset, and Testing Whether a Variable Exists
1. The Problem
puts "limit is $limit"can't read "limit": no such variableThe script stops. For a typo that is exactly right — Chapter 3.2 §8 made the case.
But sometimes the variable is genuinely optional. Perhaps it is set by a configuration file that may not exist, or by an earlier step that may not have run. You need to ask rather than assume.
2. info exists
It returns 1 or 0:
puts "before: [info exists limit]"
set limit 10
puts "after set: [info exists limit]"
unset limit
puts "after unset: [info exists limit]"before: 0
after set: 1
after unset: 0Note what it does not do: it does not read the variable, so asking about a missing one is safe. That is the whole point — it is the one way to enquire without risking the error.
3. Existing and Empty Are Different
A variable can exist and hold nothing:
set found ""
puts "exists: [info exists found]"
puts "is empty: [expr {$found eq \"\"}]"
unset found
puts "after unset, exists: [info exists found]"exists: 1
is empty: 1
after unset, exists: 0Three states, not two: not set, set but empty, and set to something. Collapsing the first two is a common source of bugs — "no value was supplied" and "the supplied value was blank" often need different handling.
4. The Default-Value Idiom
This is the pattern you will write most often:
if {![info exists limit]} {
set limit 10
}
puts "limit = $limit"limit = 10And when the variable was already set, the default is skipped:
set limit 99
if {![info exists limit]} {
set limit 10
}
puts "limit = $limit"limit = 99Read it as "if it is not already set, set it". The ! negates, and if is Module 8 — you only need to know it runs the braced body when the condition is true.
5. unset Removes a Variable
set temp "working value"
puts "exists: [info exists temp]"
unset temp
puts "after unset: [info exists temp]"exists: 1
after unset: 0After unset, the variable is gone — not empty, gone. Reading it is an error again.
You can remove several at once:
set a 1
set b 2
set c 3
unset a b
puts "a exists: [info exists a]"
puts "c exists: [info exists c]"a exists: 0
c exists: 16. Removing Something That Is Not There
Here is the surprise:
unset notherecan't unset "nothere": no such variableAn error. If you expected "remove it if it is there", Tcl does not assume that.
The flag that gives you that behaviour is -nocomplain:
unset -nocomplain nothere
puts "survived"survivedUse -nocomplain when the variable is genuinely optional, and plain unset when its absence would mean something has gone wrong — the error is useful in that case.
7. What This Chapter Does Not Cover
Two related things, deliberately left for later so you do not get a half-answer now:
- Array elements.
info existscan ask about a single element of an array, which is a different question from whether the array exists. Module 7. - Scope. A variable can exist in one place and not another — inside a procedure you may not see a variable that exists outside it.
info existsanswers about the scope you are currently in. Module 11 deals with scope properly, and it is the most common reasoninfo existsreturns 0 for a variable that visibly exists elsewhere in the file.
8. Common Mistakes
Writing info exists $name. Asks about the wrong variable. §2.
Treating missing and empty as the same. Three states, not two. §3.
Plain unset on an optional variable. Errors. Use -nocomplain. §6.
Using info exists to work around a typo. If a variable "sometimes does not exist" and you cannot say why, the check is hiding the real problem rather than solving it.
Expecting it to see other scopes. §7.
9. Exercises
Exercise 1 — the three states. Write a script that prints, for the same variable name, the result of info exists when it is unset, when it is set to the empty string, and when it is set to hello.
Exercise 2 — the default idiom. Write the default-value pattern for a variable retries defaulting to 3. Run it twice: once with retries unset, and once with it already set to 7. Confirm both behave correctly.
Exercise 3 — cause both unset errors. Run unset nothere, read the error, then run unset -nocomplain nothere and confirm it is silent.
Exercise 4 — distinguish empty from missing. Write a procedure-free script that prints not supplied when a variable does not exist, supplied but blank when it exists and is empty, and the value otherwise. Test all three cases.
Exercise 5 — read the trap. Predict what this prints, then run it:
set name limit
set limit 5
puts [info exists name]
puts [info exists $name]
puts [info exists nothing]Explain the second line using §2.
10. Remember
- Reading a missing variable is an error. That is useful default behaviour.
info exists namereturns 1 or 0 and never reads the variable, so it is always safe to ask.- No dollar sign on the name —
info exists $nameasks about a different variable. - Existing and empty are different. Three states: not set, set but empty, set to something.
- The default idiom is
if {![info exists x]} { set x <default> }. unsetremoves a variable, and takes several names at once.- Plain
unseton a missing variable is an error;unset -nocomplainis the forgiving form. - Scope and array elements are later modules — they are the usual reason a surprising 0 comes back.
Next: when a value counts as a number, and how to find out before you rely on it.
Where this fits
Part of the Tcl curriculum.
