Now that the Tcl syntax and variables have been been dealt with, we will now look at some of the commands that are available.
Each command when executed returns a value. The return value will be described along with the command.
A quick word about the notation used to describe Tcl commands. In general, a description of a command is the name of the command followed by its arguments separated by spaces. An example is:
set varName ?value?
which is a description of the Tcl set command, which takes a variable name varName and an optional argument, a value.
Optional arguments are enclosed in question mark, ?, pairs, as in the example.
A series of three dots ... represents repeated
arguments. An example is a description of the unset
command:
unset varName ?varName varName ...?
which shows that the unset
command has at least one compulsory
argument varName but has any number of subsequent optional
arguments.
The most used command over variables is the set
command.
It has the form
set varName ?value?
The value of value is determined, the variable varName is set to it, and the value is returned. If there is no value argument, the value of the variable is simply returned. It is thus used to set and/or get the value of a variable.
The unset
command is used to remove variables completely from
the system:
unset varName ?varName varName ...?
which given a series of variable names deletes them. The empty string is always returned.
There is a special command for incrementing the value of a variable:
incr varName ?increment?
which, given the name of a variable thats value is an integer string,
increments it by the amount increment. If the increment part
is left out, it defaults to 1
. The return value is the new value of
the variable.
Expressions are constructed from operands and operators and can
then be evaluated. The most general expression evaluator in Tcl is the
expr
command:
expr arg ?arg arg ... arg?
which evaluates its arguments as an expression and returns the value of the evaluation.
A simple example expression is
expr 2 * 2
which when executed returns the value 4
.
There are different classes of operators: arithmetic, relational, logical, bitwise, and choice. Here are some example expressions involving various operators:
arithmetic | $x * 2
|
relational | $x > 2
|
logical | ($x == $y) || ($x == $z)
|
bitwise | 8 & 2
|
choice | ($a == 1) ? $x : $y
|
Basically the operators follow the syntax and meaning of their ANSI C counterparts.
Expressions to the expr
command can be contained in curly brackets
in which case the usual substitutions are not done before the
expr
command is evaluated, but the command does its own round of
substitutions. So evaluating a script such as:
set a 1 expr { ($a==1) : "yes" ? "no" }
will evaluate to yes
.
Tcl also has a whole host of math functions that can be used in expressions. Their evaluation is again the same as that for their ANSI C counterparts. For example:
expr { 2*log($x) }
will return 2 times the natural log of the value of variable x
.
Tcl has a notion of lists, but as with everything it is implemented through strings. A list is a string that contains words.
A simple list is just a space separated series of strings:
set a {one two three four five}
will set the variable a
to the list containing the five
strings shown. The empty list is denoted by an open and close curly bracket
pair with nothing in between: {}
.
For the Prolog programmer, there is much confusion between a Prolog implementation of lists and the Tcl implementation of lists. In Prolog we have a definite notion of the printed representation of a list: a list is a sequence of terms enclosed in square brackets (we ignore dot notation for now); a nested list is just another term.
In Tcl, however, a list is really just a string that conforms to a certain syntax: a string of space separated words. But in Tcl there is more than one way of generating such a string. For example,
set fred {a b c d}
sets fred
to
"a b c d"
as does
set fred "a b c d"
because {a b c d}
evaluates to the string a b c d
, which
has the correct syntax for a list. But what about nested lists?
Those are represented in the final list-string as being contained in
curly brackets. For example:
set fred {a b c {1 2 3} e f}
results in fred
having the value
"a b c {1 2 3} e f"
The outer curly brackets from the set
command have disappeared,
which causes confusion. The curly brackets within a list denote a nested
list, but there are no curly brackets at the top-level of the list. (We
can't help thinking that life would have been easier if the creators of
Tcl would have chosen a consistent representation for lists, as Prolog
and LISP do.)
So remember: a list is really a string with a certain syntax, space separated items or words; a nested list is surrounded by curly brackets.
There are a dozen commands that operate on lists.
concat ?list list ...?
This makes a list out of a series of lists by concatenating its argument lists together. The return result is the list resulting from the concatenation.
lindex list index
returns the index-th element of the list. The first element of a list has an index of 0.
linsert list index value ?value ...?
returns a new list in which the value arguments have been inserted in turn before the index-th element of list.
list ?value value ...?
returns a list where each element is one of the value arguments.
llength list
returns the number of elements in list list.
lrange list first last
returns a slice of a list consisting of the elements of the list list from index first until index last.
lreplace list first last ?value ... value?
returns a copy of list list but with the elements between indices first and last replaced with a list formed from the value arguments.
lsearch ?-exact? ?-glob? ?-regexp? list pattern
returns the index of the first element in the list that matches the given pattern. The type of matching done depends on which of the switch is present -exact, -glob, -regexp, is present. Default is -glob.
lsort ?-ascii? ?-integer? ?-real? ?-command command? ?-increasing? ?-decreasing{? list
returns a list, which is the original list list sorted by the chosen technique. If none of the switches supplies the intended sorting technique, the user can provide one through the -command command switch.
There are also two useful commands for converting between lists and strings:
join list ?joinString?
which concatenates the elements of the list together, with the separator joinString between them, and returns the resulting string. This can be used to construct filenames; for example:
set a {{} usr local bin} set filename [join $a /]
results in the variable filename
having the value /usr/local/bin
.
The reverse of the join
command is the split
command:
split string ?splitChars?
which takes the string string and splits it into string on splitChars boundaries and returns a list with the strings as elements. An example is splitting a filename into its constituent parts:
set a [split /usr/local/src /]
gives a
the value {{} usr local src}
, a list.
Tcl has the four usual classes of control flow found in most other programming languages:
if...elseif...else, while, for, foreach, switch, and eval.
We go through each in turn.
The general form of an if
command is the following:
if test1 body1 ?elseif test2 body2 elseif ...? ?else bodyn?
which when evaluated, evaluates expression test1, which if true
causes body1 to be evaluated, but if false, causes test2 to
be evaluated, and so on. If there is a final else
clause,
its bodyn part is evaluated if all of the preceding tests
failed. The return result of an if
statement is the result of
the last body command evaluated, or the empty list if none of the
bodies are evaluated.
Conditional looping is done through the while
command:
while test body
which evaluates expression test, which if true then evaluates body. It continues to do that until test evaluates to 0, and returns the empty string.
A simple example is:
set a 10 while {$a > 0} { puts $a; incr a -1 }
which initializes variable a
with value ten and then
loops printing out the value of a
and decrementing it until
its value is 0, when the loop terminates.
The for
loop has the following form:
for init test reinit body
which initializes the loop by executing init, then
each time around the loop the expression test is evaluated,
which if true causes body to be executed and then executes
reinit. The loop spins around until test evaluates to 0.
The return result of a for
loop is the empty string.
An example of a for
loop:
for {set a 10} ($a>0) {incr a -1} {puts $a}
which initializes the variable a
with value 10
, then
goes around the loop printing the value of a
and decrementing it
as long as its value is greater than 0
.
Once it reaches 0
the loop terminates.
The foreach
command has the following form:
foreach varName list body
where varName is the name of a variable, list is an instance
of a list, and body is a series of commands to evaluate. A
foreach
then iterates over the elements of a list, setting the
variable varName to the current element, and executes body.
The result of a foreach
loop is always the empty string.
An example of a foreach
loop:
foreach friend {joe mary john wilbert} {puts "I like $friend"}
will produce the output:
I like joe I like mary I like john I like wilbert
There are also a couple of commands for controlling the flow of loops:
continue
and break
.
continue
stops the current evaluation of the body of a loop and
goes on to the next one.
break
terminates the loop altogether.
Tcl has a general switch statement, which has two forms:
switch ?options? string pattern body ?pattern body ... ? switch ?options? string { pattern body ?pattern body ...? }
When executed, the switch command matches its string argument against each of the pattern arguments, and the body of the first matching pattern is evaluated. The matching algorithm depends on the options chosen, which can be one of
-exact | use exact matching
|
-glob | use glob-style matching
|
-regexp | use regular expression matchinig
|
An example is:
set a rob switch -glob $a { a*z { puts "A to Z"} r*b { puts "rob or rab"} }
which will produce the output:
rob or rab
There are two forms of the switch
command. The second form has
the command arguments surrounded in curly brackets. This is
primarily so that multi-line switch commands can be formed, but it also
means that the arguments in brackets are not evaluated (curly
brackets suppress evaluation), whereas in the first type of switch
statement the arguments are first evaluated before the switch is
evaluated. These effects should be borne in mind when choosing which
kind of switch statement to use.
The final form of control statement is eval
:
eval arg ?arg ...?
which takes one or more arguments, concatenates them into a string, and executes the string as a command. The return result is the normal return result of the execution of the string as a command.
An example is
set a b set b 0 eval set $a 10
which results in the variable b
being set to 10
. In this
case, the return result of the eval
is 10
, the result of
executing the string "set b 10"
as a command.
Tcl has several commands over strings. There are commands for searching
for patterns in strings, formatting and parsing strings (much the same
as printf
and scanf
in the C language), and general string
manipulation commands.
Firstly we will deal with formatting and parsing of strings.
The commands for this are format
and scan
respectively.
format formatString ?value value ...?
which works in a similar to C's printf
; given a format string with
placeholders for values and a series of values, return the appropriate string.
Here is an example of printing out a table for base 10 logarithms for the numbers 1 to 10:
for {set n 1} {$n <= 10} {incr n} { puts [format "log10(%d) = %.4f" $n [expr log10($n)]] }
which produces the output
ln(1) = 0.0000 ln(2) = 0.3010 ln(3) = 0.4771 ln(4) = 0.6021 ln(5) = 0.6990 ln(6) = 0.7782 ln(7) = 0.8451 ln(8) = 0.9031 ln(9) = 0.9542 ln(10) = 1.0000
The reverse function of format
is scan
:
scan string formatString varName ?varName ...?
which parses the string according to the format string and assigns the appropriate values to the variables. it returns the number of fields successfully parsed.
An example,
scan "qty 10, unit cost 1.5, total 15.0" \ "qty %d, unit cost %f, total %f" \ quantity cost_per_unit total
would assign the value 10 to the variable quantity
, 1.5 to the variable
cost_per_unit
and the value 15.0 to the variable total
.
There are commands for performing two kinds of pattern matching on strings: one for matching using regular expressions, and one for matching using UNIX-style wildcard pattern matching (globbing).
The command for regular expressions matching is as follows:
regexp ?-indices? ?-nocase? exp string ?matchVar? ?subVar subVar ...?
where exp is the regular expression and string is the string on which the matching is performed. The regexp command returns 1 if the expression matches the string, 0 otherwise. The optional -nocase switch does matching without regard to the case of letters in the string. The optional matchVar and subVar variables, if present, are set to the values of string matches. In the regular expression, a match that is to be saved into a variable is enclosed in round braces. An example is
regexp {([0-9]+)} "I have 3 oranges" a
will assign the value 3 to the variable a
.
If the optional switch -indices is present, instead of storing the matching substrings in the variables, the indices of the substrings are stored; that is a list with a pair of numbers denoting the start and end position of the substring in the string. Using the same example:
regexp -indices {([0-9]+)} "I have 3 oranges" a
will assign the value "7 7"
, because the matched numeral 3
is in the eighth position in the string, and indices count from 0.
String matching using the UNIX-style wildcard pattern matching technique
is done through the string match
command:
string match pattern string
where pattern is a wildcard pattern and string is the string to match. If the match succeeds, the command returns 1; otherwise, it returns 0. An example is
string match {[a-z]*[0-9]} {a_$%^_3}
which matches because the command says match any string that starts with a lower case letter and ends with a number, regardless of anything in between.
There is a command for performing string substitutions using regular expressions:
regsub ?-all? ?-nocase? exp string subSpec varName
where exp is the regular expression and string is the input
string on which the substitution is made, subSpec is the string
that is substituted for the part of the string matched by the regular
expression, and varName is the variable on which the resulting
string is copied into. With the -nocase switch, the
matching is done without regard to the case of letters in the input
string. The -all switch causes repeated matching and
substitution to happen on the input string. The result of a
regsub
command is the number of substitutions made.
An example of string substitution is:
regsub {#name#} {My name is #name#} Rob result
which sets the variable result
to the value "My name is Rob".
An example of using the -all switch:
regsub -all {#name#} {#name#'s name is #name#} Rob result
sets the variable result
to the value "Rob's name is Rob"
and it returns the value 2 because two substitutions were made.
The are a host of other ways to manipulate strings through variants
of the string
command. Here we will go through them.
To select a character from a string given the character position,
use the string index
command. An example is:
string index "Hello world" 6
which returns w
, the 7th character of the string.
(Strings are indexed from 0).
To select a substring of a string, given a range of indices use the
string range
command. An example is:
string range "Hello world" 3 7
which returns the string "lo wo".
There is a special index marker named end
, which is used to denote the
the end of a string, so the code
string range "Hello world" 6 end
will return the string "world".
There are two ways to do simple search for a substring on a string,
using the string first
and string last
commands.
An example of string first
is:
string first "dog" "My dog is a big dog"
find the first position in string "My dog is a big dog" that matches "dog". It will return the position in the string in which the substring was found, in this case 3. If the substring cannot be found, the value -1 is returned.
Similarly,
string last "dog" "My dog is a big dog"
will return the value 16 because it returns the index of the last place in the string that the substring matches. Again, if there is no match, -1 is returned.
To find the length of a string use string length
, which given a
string simply returns its length.
string length "123456"
returns the value 6.
To convert a string completely to upper case use string toupper
:
string toupper "this is in upper case"
returns the string "THIS IS IN UPPER CASE".
Similarly,
string tolower "THIS IS IN LOWER CASE"
returns the string "this is in lower case".
There are commands for removing characters from strings:
string trim
, string trimright
, and string trimleft
.
string trim string ?chars?
which removes the characters in the string chars from the string string and returns the trimmed string. If chars is not present, whitespace characters are removed. An example is:
string string "The dog ate the exercise book" "doe"
which would return the string "Th g at th xrcis bk".
string trimleft
is the same as string trim
except only leading
characters are removed. Similarly string trimright
removes only
trailing characters.
For example:
string trimright $my_input
would return a copy of the string contained in $my_input
but with all
the trailing whitespace characters removed.
There is a comprehensive set of commands for file manipulation. We will cover only the some of the more important ones here.
To open a file the open
command is used:
open name ?access?
where name is a string containing the filename, and the option access parameter contains a string of access flags, in the UNIX style. The return result is a handle to the open file.
If access is not present, the access permissions default to
"r"
, which means open for reading only.
The command returns a file handle that can be used with other commands.
An example of the use of the open
command is
set fid [open "myfile" "r+"]
which means open the file myfile
for both reading and writing
and set the variable fid
to the file handle returned.
To close a file simply use
close fileId
For example,
close $fid
will close the file that has the file handle stored in the variable
fid
.
To read from a file, the read
command is used:
read fileId numBytes
which reads numBytes bytes from the file attached to file handle fileId, and returns the bytes actually read.
To read a single line from a file use gets
:
gets fileId ?varName?
which reads a line from the file attached to file handle fileId but chops off the trailing newline. If variable varName is specified, the string read in is stored there and the number of bytes is returned by the command. If the variable is not specified, the command returns the string only.
To write to a file, use puts
:
puts ?-nonewline? ?fileId? string
which outputs the string string. If the file handle fileId
is present, the string is output to that file; otherwise, it is
printed on stdout
. If the switch -nonewline is present,
a trailing newline is not output.
To check if the end of a file has been reached, use eof
:
eof fileId
which, given a file handle fileId returns 1 if the end has been reached, and 0 otherwise.
The are a host of other commands over files and processes, which we will not go into here.
(For extra information on file I/O commands, refer to the Tcl manual pages.)
Tcl provides a way of creating new commands, called procedures, that can be executed in scripts. The arguments of a procedure can be call-by-value or call-by-reference, and there is also a facility for creating new user defined control structures using procedures.
A procedure is declared using the proc
command:
proc name argList body
where the name of the procedure is name, the arguments are contained in argList and the body of the procedure is the script body. An example of a procedure is:
proc namePrint { first family } { puts "My first name is $first" puts "My family name is $family" }
which can be called with
namePrint Tony Blair
to produce the output:
My first name is Tony My family name is Blair
A procedure with no arguments is specified with an empty argument list. An example is a procedure that just prints out a string:
proc stringThing {} { puts "I just print this string" }
Arguments can be given defaults by pairing them with a value in a list. An example here is a counter procedure:
proc counter { value { inc 1 } } { eval $value + $inc }
which can be called with two arguments like this
set v 10 set v [counter $v 5]
which will set variable v
to the value 15; or it can be called
with one argument:
set v 10 set v [counter $v]
in which case v
will have the value 11, because the default of
the argument inc
inside the procedure is the value 1.
There is a special argument for handling procedures with variable
number of arguments, the args
argument. An example
is a procedure that sums a list of numbers:
proc sum { args } { set result 0; foreach n $args { set result [expr $result + $n ] } return $result; }
which can be called like this:
sum 1 2 3 4 5
which returns the value 15.
The restriction on using defaulted arguments is that all the
arguments that come after the defaulted ones must also be
defaulted. If args
are used, it must be the last argument
in the argument list.
A procedure can return a value through the return
command:
return ?options? ?value?
which terminates the procedure returning value value, if specified, or just causes the procedure to return, if no value specified. (The ?options? part has to do with raising exceptions, which we will will not cover here.)
The return result of a user defined procedure is the return result of the last command executed by it.
So far we have seen the arguments of a procedure are passed using
the call-by-value mechanism. They can be passed call by reference using
the upvar
command:
upvar ?level? otherVar1 myVar1 ?otherVar2 myVar2 ...?
which makes accessible variables somewhere in a calling context with the current context. The optional argument level describes how many calling levels up to look for the variable. This is best shown with an example:
set a 10 set b 20 proc add { first second } { upvar $first f $second s expr $f+$s }
which when called with
add a b
will produce the result 30. If you use call-by-value instead:
add $a $b
the program will fail because when executing the procedure
add
it will take the first argument 10 as the level
argument, a bad level. (Also variable 20
doesn't exist at
any level.)
New control structures can be generated using the uplevel
command:
uplevel ?level? arg ?arg arg ...?
which is like eval
, but it evaluates its arguments in a
context higher up the calling stack. How far up the stack to go is given
by the optional level argument.
proc do { loop condition } { set nostop 1 while { $nostop } { uplevel $loop if {[uplevel "expr $condition"] == 0} { set nostop 0 } } }
which when called with this
set x 5 do { puts $x; incr x -1 } { $x > 0 }
will print
5 4 3 2 1
(Please note: this doesn't quite work for all kinds of calls because of
break
, continue
, and return
. It is possible to get
around these problem, but that is outside the scope of this tutorial.)
A word about the scope of variables. Variables used within procedures are normally created only for the duration of that procedure and have local scope.
It is possible to declare a variable as having global scope, through the
global
command:
global name1 ? name2 ...?
where name1, name2, ..., are the names of global variables. Any references to those names will be taken to denote global variables for the duration of the procedure call.
Global variables are those variables declared at the topmost calling
context. It is possible to run a global
command at anytime in a
procedure call. After such a command, the variable name will refer to a global
variable until the procedure exits.
An example:
set x 10 proc fred { } { set y 20 global x puts [expr $x + $y] } fred
will print the result 30
where 20 comes from the local variable
y
and 10 comes from the global variable x
.
Without the global x
line, the call to fred
will fail with
an error because there is no variable x
defined locally in the
procedure for the expr
to evaluate over.
In common with other scripting languages, there is a command for evaluating the contents of a file in the Tcl interpreter:
source fileName
where fileName is the filename of the file containing the Tcl source to be evaluated. Control returns to the Tcl interpreter once the file has been evaluated.