I’ve been improving my knowledge of TCL since I’m starting to work with a lot of iRules (which are based on TCL) in my current position. With that said, here’s some notes I took from my latest round of studying and learning.
Many thanks to Exploring Expect by Don Libes for facilitating my learning!
Getting setup
Installing TCL on Ubuntu: apt install tcl
Launching the TCL shell: tclsh
Variables
Setting a variable: set i 100
Unsetting a variable: unset i
Returning a variable’s value: set i
or $i
Example: puts "Hello there, $var_name!"
or puts "Hello there, [set var_name]!"
Braces
Braces defer execution/interpretation so variables do not get interpreted. Notice how var2
returns a literal string whereas var1
does not.
% set b "foo"
foo
% set c "bar"
bar
% set var1 "a$b[set c]\r"
afoobar
% set var2 {a$b[set c]\r}
a$b[set c]\r
%
Loops
While loops
The wrong way to while loop (with sqaure brackets!):
% set i 1; while [$i < 10] { puts $i; incr i }
invalid command name "1"
%
The right way to while loop (with braces!!):
% set i 1; while {$i < 10} { puts $i; incr i }
1
2
3
4
5
6
7
8
9
%
While loops take two elements:
- Controlling expression
- Body
The while
command itself evaluates the controlling expression. That’s why you must use braces instead of square brackets so that the expression is passed into while
for evaluation on each iteration through the loop.
For loops
The for
command take three items:
- The start
- The expression
- The next
Example:
for {set count 1} {$count < 100} {incr count} {
puts [expr $count]
}
You can leave the start or next arguments blank, but you need their placeholders like so:
for {} {1} {} {
puts "Blah!"
}
Important: Unlike while
and for
commands, if
can (optionally) be used without braces because it only needs to be evaluated once. (while
and for
are evaluated on every loop.)
Thus, these are the same:
% if {1} {
puts "True!"
}
True!
%
% if 1 {
puts "Still true!"
}
Still true!
%
Incrementing variables
Incrementing variables can be done two ways (the hard way and the easy way):
% set j 10
10
% set j [expr $j + 1]
11
% incr j
12
Likewise, you can decrement variables using similar methods (note: there is no decr
function. You use incr
with a negative step value):
% set j [expr $j - 1]
11
% incr j -1
10
%
Commands & double-dash (--)
Command arguments should always be terminated with a --
(double dash) to prevent them from being interpreted as arguments.
Example 1: -force
is interpreted as an argument rather than a filename.
root@expect1:~# echo "this is a file" > -force
root@expect1:~# ls
-force
root@expect1:~# tclsh
% file copy -force b.txt
wrong # args: should be "file copy ?-option value ...? source ?source ...? target"
Using --
fixes this:
% file copy -- -force b.txt
% ^C
root@expect1:~# ls
-force b.txt
Example 2: The string -glob abc
is interpreted as an invalid option for switch
rather than as part of the search string.
% set myvar "-glob abc"
-glob abc
% switch $myvar \
"-glob abc" {
puts "Matched -glob abc"
} default {
puts "Matched default"
}
bad option "-glob abc": must be -exact, -glob, -indexvar, -matchvar, -nocase, -regexp, or --
Again, using --
fixes this:
% switch -- $myvar \
"-glob abc" {
puts "Matched -glob abc"
} default {
puts "Matched default"
}
Matched -glob abc
Functions
Defining a function is straightforward:
% proc f {a b c} {
puts "a is: $a"
puts "b is: $b"
puts "c is: $c"
}
%
So is calling it:
% f 1 2 3
a is: 1
b is: 2
c is: 3
%
Procedures share a single namespace and may be called within themselves. Weeee!
% proc death {i} {
puts $i
death [incr i]
}
% death 1
1
2
3
...
997
998
999
too many nested evaluations (infinite loop?)
%
Lists
Here are some helpful list commands:
- lindex - return item at index (zero indexed)
- lrange - return range of items
- llength - return length of list
- foreach - iterates over a list
Here is how they are used:
lindex:
% set l "This is a list of strings"
This is a list of strings
% lindex $l 0
This
% lindex $l 3
list
% lindex $l 100
lrange:
% lrange $l 1 4
is a list of
llength:
% llength $l
6
foreach:
% foreach element $l {
puts $element
}
This
is
a
list
of
strings
%
Note: After using foreach
, element
remains set to what the last element was!
% puts $element
strings
You can nest elements in a list using either double quotes or curly braces, but braces are easier to read.
Compare this…
% set x "\"a b\" c d"
"a b" c d
% foreach i $x { puts $i }
a b
c
d
%
…with this:
% set z "{a b} c d"
{a b} c d
% foreach i $z {puts $i }
a b
c
d
%
list
takes all of its arguments and combines them into a list.
% list a b c "Hi, ma"
a b c {Hi, ma}
%
% lindex [list a b c "Hi, ma"] 3
Hi, ma
%
Appending to a list can be done (properly) in two ways. (Hint: the second method is easier and better.)
Method 1:
% puts $a
foo bar
% puts $b
bar foo
% set l1 $a
foo bar
% set l1 "$l1 {$b}"
foo bar {bar foo}
%
Method 2:
% set l2 $a
foo bar
% lappend l2 $b
foo bar {bar foo}
%