Skip to content
VLSI Mentor

Tcl Part I · Module 2 · The Tcl Execution Model

Command Substitution: [ ... ]

Square brackets run a command and put its result where the brackets were. That is all they do, and it is the single feature that lets Tcl stay small — because a language that can put any command's result anywhere does not need separate syntax for calling functions. This chapter teaches you to read brackets as evaluation order: the inner command finishes first, and what it returns becomes a word in the outer command. If you arrived from C or Python, it also clears up the most common wrong guess, which is that brackets index something.

Foundation13 min readTclPart IExecution ModelSubstitutionEvaluation Order

Part I · Module 2 · Chapter 2.5 · Command Substitution: [ ... ]

1. First, What They Are Not

In C, Python and SystemVerilog, [ ] means indexing — pick element n out of something.

In Tcl it never means that. Nothing in Tcl indexes with square brackets. Getting an element out of a list uses a command called lindex, which you meet in Module 6.

In Tcl, [ ] means: run the command inside, and use what it returns.

Clearing that up now saves a lot of confusion, because the wrong reading of [string length $x] — "something indexed by string length" — produces no useful meaning at all.

2. What They Do

Azvya Education Pvt. Ltd.VLSI Mentor
basic.tcl
puts [string length "hello"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
5

Two things happened, in order:

  1. Tcl ran string length "hello", which returned 5.
  2. Tcl put 5 where the brackets were, so the outer command became puts 5.

The brackets did not survive. They were an instruction, like quotes are.

3. Why This Makes Tcl Small

Most languages need syntax for calling a function and using its value: print(len("hello")). The parentheses are grammar.

Tcl already has a way to run a command. Brackets just say "run one here and use the answer". So Tcl needs no function-call syntax, no operator for it, and no special case in the parser. One mechanism covers every command that returns anything.

That is why Chapter 2.1 could say Tcl has so little syntax. This is the feature that pays for it.

4. Tracing One

Take a line with both kinds of substitution:

Azvya Education Pvt. Ltd.VLSI Mentor
trace.tcl
set word "hello"
puts [string toupper $word]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
HELLO

Step by step:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Source text:
    puts [string toupper $word]

Words:
    word 1:  puts
    word 2:  [string toupper $word]

Build word 2 — the brackets must be evaluated:
    inner command:  string toupper $word
    $word           →  hello
    inner becomes:  string toupper hello
    inner returns:  HELLO

Final values:
    word 1:  puts
    word 2:  HELLO

Run:
    puts HELLO

The inner command is a complete command in its own right. It gets its own words, its own substitutions, and it runs to completion before the outer command exists in final form.

5. Nesting

Because the inner command is an ordinary command, it can contain brackets too:

Azvya Education Pvt. Ltd.VLSI Mentor
nested.tcl
puts [string toupper [string range "hello world" 0 4]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
HELLO

Work outwards from the innermost:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1. string range "hello world" 0 4   →  hello
2. string toupper hello             →  HELLO
3. puts HELLO                       →  prints HELLO

The rule is the same at every level: a command inside brackets finishes before the command containing it can run.

6. Brackets Inside Quoted Text

Brackets work inside double quotes, which is how most real output is built:

Azvya Education Pvt. Ltd.VLSI Mentor
in-quotes.tcl
set width 8
puts "double width = [expr {$width * 2}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
double width = 16

The quoted word contains ordinary text and one bracketed command. Tcl ran the command, put 16 in place of the brackets, and the whole thing stayed one word.

7. The Result Is a Value, Not More Tcl

This is the part that makes brackets safe, and it follows from Chapter 2.2.

A command's result is used as a word. It is not read as Tcl source. So a command can return text that looks like a command, and nothing runs:

Azvya Education Pvt. Ltd.VLSI Mentor
result-is-a-value.tcl
proc give {} { return {[exit 1]} }

puts "got: [give]"
puts "still running"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
got: [exit 1]
still running

give returned the four characters [exit 1]. Those characters became part of a word. They did not become a command.

The same is true of spaces. A result containing a space is still one word:

Azvya Education Pvt. Ltd.VLSI Mentor
result-with-spaces.tcl
proc show {args} { puts "received [llength $args] word(s)" }

show [string tolower "TWO WORDS"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
received 1 word(s)

8. Brackets Inside Braces Do Not Run

The one place brackets are inert:

Azvya Education Pvt. Ltd.VLSI Mentor
braced.tcl
puts "length is [string length hello]"
puts {length is [string length hello]}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
length is 5
length is [string length hello]

Same characters, different grouping, completely different behaviour. The braced version passed the brackets through as ordinary text.

That is Chapter 2.8, and it is not an accident — it is how a body of code reaches a command without running first.

9. Common Mistakes

Reading brackets as indexing. §1. Use lindex for list elements, in Module 6.

Forgetting the brackets. puts string length hello passes four words to puts instead of running anything. You will get an argument error, not a wrong answer.

Expecting a returned string to execute. §7.

Deep nesting for its own sake. Three levels is usually a line that should have been two lines.

Using brackets where you wanted literal text. If you need to print [ and ], escape them or use braces — Chapter 2.6.

10. Exercises

Exercise 1 — predict the result. What does each line print?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [string length "hello"]
puts [string length hello]
puts "[string length hello]"
puts {[string length hello]}

Three of these print the same thing. Explain why the fourth is different.

Exercise 2 — trace it yourself. Write out the stages, as in §4, for:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set name "ada"
puts "Hello, [string toupper $name]!"

Then run it and check your trace against the output.

Exercise 3 — unnest it. Rewrite this as two lines using a variable, and decide which version you would rather maintain:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [string toupper [string range "hello world" 0 4]]

Exercise 4 — the forgotten brackets. Run puts string length hello and read the error. How many words did puts receive, and what does that tell you about what brackets are for?

Exercise 5 — order of operations. Predict the output, then run it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc loud {} { puts "loud ran" ; return "LOUD" }
puts "before [loud] after"

Which appears first, loud ran or the before ... after line? Explain why.

11. Remember

  • [ ] runs the command inside and uses its result. It is never indexing.
  • The inner command finishes first. Its result becomes a word in the outer command.
  • Nesting works inside-out, as deep as you like.
  • Brackets work inside double quotes, which is how most messages are built.
  • A result is a value, not source. Text that looks like a command does not run.
  • A result containing spaces is still one word.
  • Brackets inside braces do not run. Chapter 2.8.

Next: the third substitution, and the one that lets you put a literal $ or [ in your text.

Where this fits

Part of the Tcl curriculum.