TCL Tutorial
TCL Tutorial
Page 1 of 2
Tcl Tutorial
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
Introduction
Simple Text Output
Assigning values to variables
Evaluation & Substitutions 1: Grouping arguments with ""
Evaluation & Substitutions 2: Grouping arguments with {}
Evaluation & Substitutions 3: Grouping arguments with []
Results of a command - Math 101
Numeric Comparisons 101 - if
Textual Comparison - switch
Looping 101 - While loop
Looping 102 - For and incr
Adding new commands to Tcl - proc
Variations in proc arguments and return values
Variable scope - global and upvar
Tcl Data Structures 101 - The list
Adding & Deleting members of a list
More list commands - lsearch, lsort, lrange
Simple pattern matching - "globbing"
String Subcommands - length index range
String comparisons - compare match first last wordend
Modifying Strings - tolower, toupper, trim, format
Regular Expressions 101
More Examples Of Regular Expressions
More Quoting Hell - Regular Expressions 102
Associative Arrays
More On Arrays - Iterating and use in procedures
File Access 101
Information about Files - file, glob
Invoking Subprocesses from Tcl - exec, open
Learning the existence of commands and variables ? - info
State of the interpreter - info
Information about procs - info
Modularization - source
Building reusable libraries - packages and namespaces
Creating Commands - eval
More command construction - format, list
Substitution without evaluation - format, subst
Changing Working Directory - cd, pwd
Debugging & Errors - errorInfo errorCode catch error return
More Debugging - trace
Command line arguments and environment strings
Leftovers - time, unset
Channel I/O: socket, fileevent, vwait
Time and Date - clock
More channel I/O - fblocked & fconfigure
Child interpreters
file://E:\vss\Tcl Tutorial.htm
2/17/2008
Introduction
Page 1 of 2
Introduction
Index
Next lesson
Welcome to the Tcl tutorial. We wrote it with the goal of helping you to learn Tcl. It is aimed at those who have
some knowledge of programming, although you certainly don't have to be an expert. The tutorial is intended as a
companion to the Tcl manual pages which provide a reference for all Tcl commands.
It is divided into brief sections covering different aspects of the language. Depending on what system you are on,
you can always look up the reference documentation for commands that you are curious about. On Unix for
example, man while would bring up the man page for the while command.
Each section is accompanied by relevant examples showing you how to put to use the material covered.
Additional Resources
The Tcl community is an exceedingly friendly one. It's polite to try and figure things out yourself, but if you're
struggling, we're more than willing to help. Here are some good places to get help:
l
l
l
Credits
file://E:\vss\Introduction.htm
2/17/2008
Introduction
Page 2 of 2
Thanks first and foremost to Clif Flynt for making his material available under a BSD license. The following people
also contributed:
l
l
l
Neil Madden
Arjen Markus
David N. Welton
Of course, we also welcome comments and suggestions about how it could be improved - or if it's great the way it
is, we don't mind a bit of thanks, either!
Index
file://E:\vss\Introduction.htm
Next lesson
2/17/2008
Running Tcl
Page 1 of 1
Running Tcl
Index
Next lesson
When you have installed Tcl, the program you will then call to utilize it is tclsh. For instance, if you write some code
to a file "hello.tcl", and you want to execute it, you would do it like so: tclsh hello.tcl.
TODO: readline, or lack thereof, and how to get around it -> tkcon.
Index
file://E:\vss\Running Tcl.htm
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
The traditional starting place for a tutorial is the classic "Hello, World" program. Once you can print out a string,
you're well on your way to using Tcl for fun and profit!
The command to output a string in Tcl is the puts command.
A single unit of text after the puts command will be printed to the standard output device. The default behavior is to
print a newline character ("return") appropriate for the system after printing the text.
If the string has more than one word, you must enclose the string in double quotes or braces ({}). A set of words
enclosed in quotes or braces is treated as a single unit, while words separated by whitespace are treated as
multiple arguments to the command. Quotes and braces can both be used to group several words into a single unit.
However, they actually behave differently. In the next lesson you'll start to learn some of the differences between
their behaviors. Note that in Tcl, single quotes are not significant, as they are in other programming languages such
as C, Perl and Python.
Many commands in Tcl (including puts) can accept multiple arguments. If a string is not enclosed in quotes or
braces, the Tcl interpreter will consider each word in the string as a separate argument, and pass each individually
to the puts command. The puts command will try to evaluate the words as optional arguments. This will probably
result in an error.
A command in Tcl is a list of words terminated by a newline or semicolon. Tcl comments are a # at the beginning of
the line, or after the command is closed with a ; semicolon.
Example
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 6
Index
Next lesson
If you are new to programming, then this lesson may contain some surprising information. But even if you are used
to writing programs, computers can do unexpected things with numbers. The purpose of this lesson is to shed some
light on some of the mysteries and quirks you can encounter.
These mysteries exist independently of the programming language, though one programming language may be
better at isolating you from them than another. The problem is that computers can not deal with the numbers we
are used and in the way we are used to.
For instance (*):
# Compute 1 million times 1 million
% puts [expr {1000000*1000000}]
-727379968
To most people's surprise, the result is negative! Instead of 1000000000000, a negative number is returned.
Important note: I used Tcl 8.4.1 for all examples. In Tcl 8.5 the results will hopefully be more intuitive, as a result
of adding so-called big integers. Nevertheless, the general theme remains the same.
Now consider the following example, it is almost the same, with the exception of a decimal dot:
# Compute 1 million times 1 million
% puts [expr {1000000*1000000.}]
1e+012
The reason is simple, well if you know more about the background of computer arithmetic:
2/17/2008
Page 2 of 6
In the first example we multiplied two integer numbers, or short integers. While we are used to these numbers
ranging from -infinity to +infinity, computers can not deal with that range (at least not that easily). So,
instead, computers deal with a subset of the actual mathematical integer numbers. They deal with numbers
from -231 to 231-1 (in general) - that is, with numbers from -2147483648 to 2147483647.
Numbers outside that range can not be dealt with that easily. This is also true of the results of a computation
(or the intermediate results of a computation, even if the final result does fit).
In the second example we multiplied an integer with a floating-point number, in common parlance: a real
number or real (though there are very significant differences between the mathematical real numbers and the
computer's real numbers that are at the heart of another group of mysteries and quirks). Floating-point
numbers have a much larger range and they can be used to deal with such numbers as 1.2 and 3.1415026.
The typical range for floating-point numbers is roughly: -1.0e+300 to -1.0e-300, 0.0 and 1.0e-300 to
1.0e+300. Floating-point numbers have a limited precision: usually about 12 decimals.
What this means in practical terms, is that a floating-point number can be as a 1 followed by 300 zeros or as
small as 0.0000...1 (where "..." stands for 295 zeros).
Because the range is so much bigger, in the second example the result falls within the limits and we get the
answer we expected.
Tcl's strategy
Tcl uses a simple but efficient strategy to decide what kind of numbers to use for the computations:
l
If you add, subtract, multiply and divide two integer numbers, then the result is an integer. If the result fits
within the range you have the exact answer. If not, you end up with something that appears to be completely
wrong. (Note: not too long ago, floating-point computations were much more time-consuming than integer
computations. And most computers do not warn about integer results outside the range, because that is too
time-consuming too: a computer typically uses lots of such operations, most of which do fit into the
designated range.)
If you add, subtract, multiply and divide an integer number and a floating-point number, then the integer
2/17/2008
Page 3 of 6
number is first converted to a floating-point number with the same value and then the computation is done,
resulting in a floating-point number.
Floating-point computations are quite complex, and the current (IEEE) standard prescribes what should
happen in minute detail. One such detail is that results outside the proper ranges are reported. Tcl catches
these and displays a warning:
# Compute 1.0e+300/1.0-300
% puts [expr {1.0e300/1.0e-300}]
floating-point value too large to represent
The first two computations have the surprising result: 0 and -1. That is because the result is an integer number and
the mathematically exact results 1/2 and -1/2 are not.
If you interested in the details of how Tcl works, the outcome q is determined as follows:
a = q * b + r
0 <= |r| < |b|
r has the same sign as q
2/17/2008
Page 4 of 6
While many of the above computations give the result you would expect, note however the last decimals, the last
two do not give exactly 5 and 12! This is because computers can only deal with numbers with a limited precision:
floating-point numbers are not our mathematical real numbers.
Somewhat unexpectedly, 1/10 also gives problems. 1.2/0.1 results in 11.999999999999998, not 12. That is an
example of a very nasty aspect of most computers and programming languages today: they do not work with
ordinary decimal fractions, but with binary fractions. So, 0.5 can be represented exactly, but 0.1 can not.
Results obtained on one computer may not exactly match the results on another computer. Usually the
differences are small, but if you have a lot of computations, they can add up!
2/17/2008
Page 5 of 6
Whenever you convert from floating-point numbers to integer numbers, for instance when determining the
labels for a graph (the range is 0 to 1.2 and you want a stepsize of 0.1), you need to be careful:
#
# The wrong way
#
set number [expr {int(1.2/0.1)}]
#
# A right way - note the limit
#
set x
0.0
set delta 0.1
while { $x < 1.2+0.5*$delta } {
... create label $x
set x [expr {$x + $delta}]
}
l
If you want to do financial computations, take care: there are specific standards for doing such computations
that unfortunately depend on the country where they are used - the US standard is slightly different from the
European standard.
Transcendental functions, like sin() and exp() are not standardised at all. The outcome could differ in one or
more decimals from one computer to the next. So, if you want to be absolutely certain that (pi) is a specific
value, use that value and do not rely on formulae like these:
#
# Two different estimates of "pi" - on Windows 98
#
set pi1 [expr {4.0*atan(1.0)}]
set pi2 [expr {6.0*asin(0.5)}]
puts [expr {$pi1-$pi2}]
2/17/2008
Page 6 of 6
-4.4408920985006262e-016
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
In Tcl, everything may be represented as a string, although internally it may be represented as a list, integer,
double, or other type, in order to make the language fast.
The assignment command in Tcl is set.
When set is called with two arguments, as in:
set fruit Cauliflower
it places the second argument (Cauliflower) in the memory space referenced by the first argument (fruit). Set
always returns the contents of the variable named in the first argument. Thus, when set is called with two
arguments, it places the second argument in the memory space referenced by the first argument and then returns
the second argument. In the above example, for instance, it would return "Cauliflower", without the quotes.
The first argument to a set command can be either a single word, like fruit or pi , or it can be a member of an
array. Arrays will be discussed in greater detail later, for the time being just remember that many data can be
collected under a single variable name, and an individual datum can be accessed by its index within that array.
Indexing into an array in Tcl is handled by putting the index within parentheses after the name of the variable.
can also be invoked with only one argument. When called with just one argument, it will return the contents of
that argument.
Set
2/17/2008
Page 2 of 2
If value is specified, then the contents of the variable varName are set equal to value .
If varName consists only of alphanumeric characters, and no parentheses, it is a scalar variable.
If varName has the form varName(index) , it is a member of an associative array.
If you look at the example code, you'll notice that in the set command the first argument is typed with only its
name, but in the puts statement the argument is preceded with a $.
The dollar sign tells Tcl to use the value of the variable - in this case X or Y.
Tcl passes data to subroutines either by name or by value. Commands that don't change the contents of a variable
usually have their arguments passed by value. Commands that do change the value of the data must have the data
passed by name.
Example
set X "This is a string"
set Y 1.24
puts $X
puts $Y
puts "..............................."
set label "The value in Y is: "
puts "$label $Y"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
This lesson is the first of three which discuss the way Tcl handles substitution during command evaluation.
In Tcl, the evaluation of a command is done in 2 phases. The first phase is a single pass of substitutions. The
second phase is the evaluation of the resulting command. Note that only one pass of substitutions is made. Thus in
the command
puts $varName
the contents of the proper variable are substituted for $varName, and then the command is executed. Assuming we
have set varName to "Hello World", the sequence would look like this: puts $varName puts "Hello World", which is
then executed and prints out Hello World.
During the substitution phase, several types of substitutions occur.
A command within square brackets ([]) is replaced with the result of the execution of that command. (This will be
explained more fully in the lesson "Results of a Command - Math 101.")
Words within double quotes or braces are grouped into a single argument. However, double quotes and braces
cause different behavior during the substitution phase. In this lesson, we will concentrate on the behavior of double
quotes during the substitution phase.
Grouping words within double quotes allows substitutions to occur within the quotations - or, in fancier terms,
"interpolation". The substituted group is then evaluated as a single argument. Thus, in the command:
puts "The current stock value is $varName"
the current contents of varName are substituted for $varName, and then the entire string is printed to the output
file://E:\vss\Evaluation & Substitutions 1 Grouping arguments with.htm
2/17/2008
Page 2 of 3
\xHH....
Output
Audible Bell
Backspace
Form Feed (clear screen)
New Line
Carriage Return
Tab
Vertical Tab
Octal Value
H is a hex digit 0-9,AF,a-f. This represents a
16-byte Unicode
character.
Hex Value
Hex Value
0x07
0x08
0x0c
0x0a
0x0d
0x09
0x0b
d is a digit from 0-7
The final exception is the backslash at the end of a line of text. This causes the interpreter to ignore the newline,
and treat the text as a single line of text. The interpreter will insert a blank space at the location of the ending
backslash.
2/17/2008
Page 3 of 3
Example
set Z Albany
set Z_LABEL "The Capitol of New York is: "
puts "$Z_LABEL $Z"
puts "$Z_LABEL \$Z"
;#
;#
;#
;#
This
This
This
But,
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
During the substitution phase of command evaluation, the two grouping operators, the brace ({) and the double
quote ("), are treated differently by the Tcl interpreter.
In the last lesson you saw that grouping words with double quotes allows substitutions to occur within the double
quotes. By contrast, grouping words within double braces disables substitution within the braces. Characters
within braces are passed to a command exactly as written. The only "Backslash Sequence" that is processed within
braces is the backslash at the end of a line. This is still a line continuation character.
Note that braces have this effect only when they are used for grouping (i.e. at the beginning and end of a sequence
of words). If a string is already grouped, either with quotes or braces, and braces occur in the middle of the
grouped string (i.e. "foo{bar"), then the braces are treated as regular characters with no special meaning. If the
string is grouped with quotes, substitutions will occur within the quoted string, even between the braces.
Example
set Z Albany
set Z_LABEL "The Capitol of New York is: "
puts "\n................. examples of differences between
puts "$Z_LABEL $Z"
puts {$Z_LABEL $Z}
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
You obtain the results of a command by placing the command in square brackets ([]). This is the functional
equivalent of the back single quote (`) in sh programming, or using the return value of a function in C.
As the Tcl interpreter reads in a line it replaces all the $variables with their values. If a portion of the string is
grouped with square brackets, then the string within the square brackets is evaluated as a command by the
interpreter, and the result of the command replaces the square bracketed string.
Let's take the following code segment, for example:
puts [readsensor [selectsensor]]
l
l
l
l
l
The parser scans the entire command, and sees that there is a command substitution to perform: readsensor
[selectsensor] , which is sent to the interpreter for evaluation.
The parser once again finds a command to be evaluated and substituted, selectsensor
The fictitious selectsensor command is evaluated, and it presumably returns a sensor to read.
At this point, readsensor has a sensor to read, and the readsensor command is evaluated.
Finally, the value of readsensor is passed on back to the puts command, which prints the output to the screen.
Example
2/17/2008
Page 2 of 2
set x abc
puts "A simple substitution: $x\n"
set y [set x "def"]
puts "Remember that set returns the new value of the variable: X: $x Y: $y\n"
set z {[set x "This is a string within quotes within braces"]}
puts "Note the curly braces: $z\n"
set a "[set x {This is a string within braces within quotes}]"
puts "See how the set is executed: $a"
puts "\$x is: $x\n"
set b "\[set y {This is a string within braces within quotes}]"
# Note the \ escapes the bracket, and must be doubled to be a
# literal character in double quotes
puts "Note the \\ escapes the bracket:\n \$b is: $b"
puts "\$y is: $y"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 6
Index
Next lesson
The Tcl command for doing math type operations is expr. The following discussion of the expr command is extracted
and adapted from the expr man page. Many commands use expr behind the scenes in order to evaluate test
expressions, such as if, while and for loops, discussed in later sections. All of the advice given here for expr also
holds for these other commands.
takes all of its arguments ("2 + 2" for example) and evaluates the result as a Tcl "expression" (rather than a
normal command), and returns the value. The operators permitted in Tcl expressions include all the standard math
functions, logical operators, bitwise operators, as well as math functions like rand(), sqrt(), cosh() and so on.
Expressions almost always yield numeric results (integer or floating-point values).
expr
Performance tip: enclosing the arguments to expr in curly braces will result in faster code. So do expr {$i * 10}
instead of simply expr $i * 10
WARNING: You should always use braces when evaluating code that may contain user input, to avoid
possible security breaches. To illustrate the danger, consider this interactive session:
% set userinput {[puts DANGER!]}
[puts DANGER!]
% expr $userinput == 1
DANGER!
0
% expr {$userinput == 1}
0
In the first example, the code contained in the user-supplied input is evaluated, whereas in the second the
braces prevent this potential danger. As a general rule, always surround expressions with braces, whether
using expr directly or some other command that takes an expression.
file://E:\vss\Results of a command - Math 101.htm
2/17/2008
Page 2 of 6
OPERANDS
A Tcl expression consists of a combination of operands, operators, and parentheses. White space may be used
between operands, operators and parentheses; it is ignored by the expression processor. Where possible, operands
are interpreted as integer values. Integer values may be specified in decimal (the normal case), in octal (if the first
character of the operand is 0), or in hexadecimal (if the first two characters of the operand are 0x).
Note that the octal and hexadecimal conversion takes place differently in the expr command than in the Tcl
substitution phase. In the substitution phase, a \x32 would be converted to an ascii "2", while expr would convert
0x32 to a decimal 50.
If an operand does not have one of the integer formats given above, then it is treated as a floating-point number, if
that is possible. Floating-point numbers may be specified in any of the ways accepted by an ANSI-compliant C
compiler. For example, all of the following are valid floating-point numbers:
2.1
3.
6E4
7.91e+16
.000001
If no numeric interpretation is possible, then an operand is left as a string (and only a limited set of operators may
be applied to it).
Note however, that it does not support numbers of the following forms:
2,1
2,100
It is possible to deal with numbers in that form, but you will have to convert these "strings" to numbers in the
standard form first.
2/17/2008
Page 3 of 6
Beware of leading zeros: 0700 is not interpreted as the decimal number 700 (seven hundred), but as the octal
number 700 = 7*8*8 = 448 (decimal).
Worse, if the number contains a digit 8 or 9 an error results:
% expr {0900+1}
expected integer but got "0900" (looks like invalid octal number)
Octal numbers are in fact a relic of the past, when such number formats were much more common.
Operands may be specified in any of the following ways:
l
l
OPERATORS
The valid operators are listed below, grouped in decreasing order of precedence:
-+~!
Unary minus, unary plus, bit-wise NOT, logical NOT. None of these operators may be applied to string
operands, and bit-wise NOT may be applied only to integers.
**
Exponentiation (works on both floating-point numbers and integers)
*/%
Multiply, divide, remainder. None of these operators may be applied to string operands, and remainder may be
applied only to integers. The remainder will always have the same sign as the divisor and an absolute value
smaller than the divisor.
+Add and subtract. Valid for any numeric operands.
<< >>
Left and right (bit) shift. Valid for integer operands only.
< > <= >=
2/17/2008
Page 4 of 6
Relational operators: less, greater, less than or equal, and greater than or equal. Each operator produces 1 if
the condition is true, 0 otherwise. These operators may be applied to numeric operands as well as strings, in
which case string comparison is used.
eq ne in ni
compare two strings for equality (eq) or inequality (ne). and two operators for checking if a string is contained
in a list (in) or not (ni). These operators all return 1 (true) or 0 (false). Using these operators ensures that the
operands are regarded exclusively as strings (and lists), not as possible numbers:
% expr { "9" == "9.0"}
1
% expr { "9" eq "9.0"}
0
&
^
|
&&
||
Logical OR. Produces a 0 result if both operands are zero, 1 otherwise. Valid for numeric operands only
(integers or floating-point).
x?y:z
If-then-else. If x evaluates to non-zero, then the result is the value of y. Otherwise the result is the value of z.
The x operand must have a numeric value.
% set x 1
% expr { $x>0? ($x+1) : ($x-1) }
2
MATH FUNCTIONS
file://E:\vss\Results of a command - Math 101.htm
2/17/2008
Page 5 of 6
cosh
double
exp
floor
fmod
hypot
int
log
log10
pow
rand
round
sin
sinh
sqrt
srand
tan
tanh
wide
Besides these functions, you can also apply commands within an expression. For instance:
% set x 1
% set w "Abcdef"
% expr { [string length $w]-2*$x }
4
TYPE CONVERSIONS
Tcl supports the following functions to convert from one representation of a number to another:
double int wide
l
l
l
The next lesson explains the various types of numbers in more detail.
Example
set X 100
set Y 256
2/17/2008
Page 6 of 6
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
The words then and else are optional, although generally then is left out and else is used.
The test expression following if should return a value that can be interpreted as representing "true" or "false":
False
a numeric value 0
yes/no
no
true/false
false
True
all others
yes
true
If the test expression returns a string "yes"/"no" or "true"/"false", the case of the return is not checked.
True/FALSE or YeS/nO are legitimate returns.
If the test expression evaluates to True, then body1 will be executed.
If the test expression evaluates to False, then the word after body1 will be examined. If the next word is elseif, then
the next test expression will be tested as a condition. If the next word is else then the final body will be evaluated as
a command.
The test expression following the word if is evaluated in the same manner as in the expr command.
The test expression following if may be enclosed within quotes, or braces. If it is enclosed within braces, it will be
2/17/2008
Page 2 of 3
evaluated within the if command, and if enclosed within quotes it will be evaluated during the substitution phase,
and then another round of substitutions will be done within the if command.
Note: This extra round can cause unexpected trouble - avoid it.
Example
set x 1
if {$x == 2} {puts "$x is 2"} else {puts "$x is not 2"}
if {$x != 1} {
puts "$x is != 1"
} else {
puts "$x is 1"
}
if $x==1 {puts "GOT 1"}
#
# Be careful, this is just an example
# Usually you should avoid such constructs,
# it is less than clear what is going on and it can be dangerous
#
set y x
if "$$y != 1" {
puts "$$y is != 1"
} else {
puts "$$y is 1"
}
#
# A dangerous example: due to the extra round of substitution,
# the script stops
#
set y {[exit]}
if "$$y != 1" {
puts "$$y is != 1"
} else {
2/17/2008
Page 3 of 3
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
The switch command allows you to choose one of several options in your code. It is similar to switch in C, except
that it is more flexible, because you can switch on strings, instead of just integers. The string will be compared to a
set of patterns, and when a pattern matches the string, the code associated with that pattern will be evaluated.
It's a good idea to use the switch command when you want to match a variable against several possible values, and
don't want to do a long series of if... elseif ... elseif statements.
The syntax of the command is:
l
- or l
is the string that you wish to test, and pattern1, pattern2, etc are the patterns that the string will be
compared to. If string matches a pattern, then the code within the body associated with that pattern will be
executed. The return value of the body will be returned as the return value of the switch statement. Only one
pattern will be matched.
String
If the last pattern argument is the string default, that pattern will match any string. This guarantees that some set
of code will be executed no matter what the contents of string are.
If there is no default argument, and none of the patterns match string, then the switch command will return an
empty string.
2/17/2008
Page 2 of 3
If you use the brace version of this command, there will be no substitutions done on the patterns. The body of the
command, however, will be parsed and evaluated just like any other command, so there will be a pass of
substitutions done on that, just as will be done in the first syntax. The advantage of the second form is that you
can write multiple line commands more readably with the brackets.
Note that you can use braces to group the body argument when using the switch or if commands. This is because
these commands pass their body argument to the Tcl interpreter for evaluation. This evaluation includes a pass of
substitutions just as it does for code not within a command body argument.
Example
set x "ONE"
set y 1
set z ONE
# This is probably the easiest and cleanest form of the command
# to remember:
switch $x {
"$z" {
set y1 [expr {$y+1}]
puts "MATCH \$z. $y + $z is $y1"
}
ONE {
set y1 [expr {$y+1}]
puts "MATCH ONE. $y + one is $y1"
}
TWO {
set y1 [expr {$y+2}]
puts "MATCH TWO. $y + two is $y1"
}
THREE {
set y1 [expr {$y+3}]
puts "MATCH THREE. $y + three is $y1"
}
default {
puts "$x is NOT A MATCH"
2/17/2008
Page 3 of 3
}
}
switch $x "$z" {
set y1 [expr {$y+1}]
puts "MATCH \$z. $y + $z is $y1"
} ONE {
set y1 [expr {$y+1}]
puts "MATCH ONE. $y + one is $y1"
} TWO {
set y1 [expr {$y+2}]
puts "MATCH TWO. $y + two is $y1"
} THREE {
set y1 [expr {$y+3}]
puts "MATCH THREE. $y + three is $y1"
} default {
puts "$x does not match any of these choices"
}
switch $x "ONE" "puts ONE=1" "TWO" "puts TWO=2" "default" "puts NO_MATCH"
switch $x \
"ONE"
"puts ONE=1" \
"TWO"
"puts TWO=2" \
"default"
"puts NO_MATCH";
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
Tcl includes two commands for looping, the while and for commands. Like the if statement, they evaluate their test
the same way that the expr does. In this lesson we discuss the while command, and in the next lesson, the for
command. In most circumstances where one of these commands can be used, the other can be used as well.
l
The while command evaluates test as an expression. If test is true, the code in body is executed. After the code in
body has been executed, testis evaluated again.
A continue statement within body will stop the execution of the code and the test will be re-evaluated. A break within
body will break out of the while loop, and execution will continue with the next line of code after body
In Tcl everything is a command, and everything goes through the same substitution phase. For this reason, the
test must be placed within braces. If test is placed within quotes, the substitution phase will replace any variables
with their current value, and will pass that test to the while command to evaluate, and since the test has only
numbers, it will always evaluate the same, quite probably leading to an endless loop!
Look at the two loops in the example. If it weren't for the break command in the second loop, it would loop forever.
Example
set x 1
# This is a normal way to write a Tcl while loop.
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
Tcl supports an iterated loop construct similar to the for loop in C. The for command in Tcl takes four arguments;
an initialization, a test, an increment, and the body of code to evaluate on each pass through the loop. The syntax
for the for command is:
for start test next body
During evaluation of the for command, the start code is evaluated once, before any other arguments are
evaluated. After the start code has been evaluated, the test is evaluated. If the test evaluates to true, then the
body is evaluated, and finally, the next argument is evaluated. After evaluating the next argument, the interpreter
loops back to the test, and repeats the process. If the test evaluates as false, then the loop will exit immediately.
is the initialization portion of the command. It is usually used to initialize the iteration variable, but can
contain any code that you wish to execute before the loop starts.
Start
The test argument is evaluated as an expression, just as with the expr while and if commands.
Next
is commonly an incrementing command, but may contain any command which the Tcl interpreter can evaluate.
Body
Since you commonly do not want the Tcl interpreter's substitution phase to change variables to their current values
before passing control to the for command, it is common to group the arguments with curly braces. When braces
are used for grouping, the newline is not treated as the end of a Tcl command. This makes it simpler to write
multiple line commands. However, the opening brace must be on the line with the for command, or the Tcl
interpreter will treat the close of the next brace as the end of the command, and you will get an error. This is
different than other languages like C or Perl, where it doesn't matter where you place your braces.
2/17/2008
Page 2 of 3
Within the body code, the commands break and continue may be used just as they are used with the while command.
When a break is encountered, the loop exits immediately. When a continue is encountered, evaluation of the body
ceases, and the test is re-evaluated.
Because incrementing the iteration variable is so common, Tcl has a special command for this:
l
This command adds the value in the second argument to the variable named in the first argument. If no value is
given for the second argument, it defaults to 1.
Example
for {set i 0} {$i < 10} {incr i} {
puts "I inside first loop: $i"
}
for {set i 3} {$i < 2} {incr i} {
puts "I inside second loop: $i"
}
puts "Start"
set i 0
while {$i < 10} {
puts "I inside first loop: $i"
incr i
puts "I after incr: $i"
}
set i 0
incr i
# This is equivalent to:
set i [expr {$i + 1}]
2/17/2008
Page 3 of 3
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
In Tcl there is actually no distinction between commands (often known as 'functions' in other languages) and
"syntax". There are no reserved words (like if and while) as exist in C, Java, Python, Perl, etc... When the Tcl
interpreter starts up there is a list of known commands that the interpreter uses to parse a line. These commands
include while, for, set, puts, and so on. They are, however, still just regular Tcl commands that obey the same
syntax rules as all Tcl commands, both built-in, and those that you create yourself with the proc command.
The proc command creates a new command. The syntax for the proc command is:
l
When proc is evaluated, it creates a new command with name name that takes arguments args. When the procedure
name is called, it then runs the code contained in body.
is a list of arguments which will be passed to name. When name is invoked, local variables with these names will
be created, and the values to be passed to name will be copied to the local variables.
Args
The value that the body of a proc returns can be defined with the return command. The return command will return
its argument to the calling program. If there is no return, then body will return to the caller when the last of its
commands has been executed. The return value of the last command becomes the return value of the procedure.
Example
proc sum {arg1 arg2} {
set x [expr {$arg1 + $arg2}];
2/17/2008
Page 2 of 2
return $x
}
puts " The sum of 2 + 3 is: [sum 2 3]\n\n"
proc for {a b c} {
puts "The for command has been replaced by a puts";
puts "The arguments were: $a\n$b\n$c\n"
}
for {set i 1} {$i < 10} {incr i}
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
A proc can be defined with a set number of required arguments (as was done with sum in the previous lesson, or it
can have a variable number of arguments. An argument can also be defined to have a default value.
Variables can be defined with a default value by placing the variable name and the default within braces within args.
For example:
proc justdoit {a {b 1} {c -1}} {
}
Since there are default arguments for the b and c variables, you could call the procedure one of three ways:
justdoit 10, which would set a to 10, and leave b set to its default 1, and c at -1. justdoit 10 20 would likewise set b
to 20, and leave C to its default.
A proc will accept a variable number of arguments if the last declared argument is the word args. If the last
argument to a proc argument list is args, then any arguments that aren't already assigned to previous variables will
be assigned to args.
The example procedure below is defined with three arguments. At least one argument *must* be present when
example is called. The second argument can be left out, and in that case it will default to an empty string. By
declaring args as the last argument, example can take a variable number of arguments.
Note that if there is a variable other than args after a variable with a default, then the default will never be used.
For example, if you declare a proc such as: proc function { a {b 1} c} {...}, you will always have to call it with 3
arguments.
2/17/2008
Page 2 of 3
Tcl assigns values to a proc's variables in the order that they are listed in the command. If you provide 2
arguments when you call function they will be assigned to a and b, and Tcl will generate an error because c is
undefined.
You can, however, declare other arguments that may not have values as coming after an argument with a default
value. For example, this is valid:
proc example {required {default1 a} {default2 b} args} {...}
In this case, example requires one argument, which will be assigned to the variable required. If there are two
arguments, the second arg will be assigned to default1. If there are 3 arguments, the first will be assigned to
required, the second to default1, and the third to default2. If example is called with more than 3 arguments, all the
arguments after the third will be assigned to args.
Example
proc example {first {second
if {$second eq ""} {
puts "There is only
return 1
} else {
if {$args eq ""} {
puts "There are
return 2
} else {
puts "There are
return "many"
}
}
}
set
set
set
set
count1
count2
count3
count4
[example
[example
[example
[example
""} args} {
one argument and it is: $first"
ONE]
ONE TWO]
ONE TWO THREE ]
ONE TWO THREE FOUR]
2/17/2008
Page 3 of 3
puts "The example was called with $count1, $count2, $count3, and $count4 Arguments"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 4
Index
Next lesson
Tcl evaluates variables within a scope delineated by procs, namespaces (see Building reusable libraries - packages
and namespaces), and at the topmost level, the global scope.
The scope in which a variable will be evaluated can be changed with the global and upvar commands.
The global command will cause a variable in a local scope (inside a procedure) to refer to the global variable of that
name.
The upvar command is similar. It "ties" the name of a variable in the current scope to a variable in a different scope.
This is commonly used to simulate pass-by-reference to procs.
You might also encounter the variable command in others' Tcl code. It is part of the namespace system and is
discussed in detail in that chapter.
Normally, Tcl uses a type of "garbage collection" called reference counting in order to automatically clean up
variables when they are not used anymore, such as when they go "out of scope" at the end of a procedure, so that
you don't have to keep track of them yourself. It is also possible to explicitly unset them with the aptly named unset
command.
The syntax for upvar is:
l
The upvar command causes myVar1 to become a reference to otherVar1, and myVar2 to become a reference to
otherVar2, etc. The otherVar variable is declared to be at level relative to the current procedure. By default level is
1, the next level up.
2/17/2008
Page 2 of 4
If a number is used for the level, then level references that many levels up the stack from the current level.
If the level number is preceded by a # symbol, then it references that many levels down from the global scope. If
level is #0, then the reference is to a variable at the global level.
If you are using upvar with anything except #0 or 1, you are most likely asking for trouble, unless you really know
what you're doing.
You should avoid using global variables if possible. If you have a lot of globals, you should reconsider the design of
your program.
Note that since there is only one global space it is surprisingly easy to have name conflicts if you are importing
other peoples code and aren't careful. It is recommended that you start global variables with an identifiable prefix
to help avoid unexpected conflicts.
Example
proc SetPositive {variable value } {
upvar $variable myvar
if {$value < 0} {
set myvar [expr {-$value}]
} else {
set myvar $value
}
return $myvar
}
SetPositive x 5
SetPositive y -5
puts "X : $x
proc two {y} {
upvar 1 $y z
Y: $y\n"
2/17/2008
Page 3 of 4
;#
;#
;#
;#
}
proc one {y} {
upvar $y z
puts "one: Z: $z"
two z
}
one y
puts "\nX: $x
2/17/2008
Page 4 of 4
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
The list is the basic Tcl data structure. A list is simply an ordered collection of stuff; numbers, words, strings, or
other lists. Even commands in Tcl are just lists in which the first list entry is the name of a proc, and subsequent
members of the list are the arguments to the proc.
Lists can be created in several ways:
by setting a variable to be a list of values
set lst {{item 1} {item 2} {item 3}}
with the split command
set lst [split "item 1.item 2.item 3" "."]
with the list command.
set lst [list "item 1" "item 2" "item 3"]
An individual list member can be accessed with the lindex command.
The brief description of these commands is:
list ?arg1? ?arg2? ... ?argN?
Splits the string into a list of items wherever the splitChars occur in the code. SplitChars defaults to being
whitespace. Note that if there are two or more splitChars then each one will be used individually to split the
string. In other words: split "1234567" "36" would return the following list: {12 45 7}.
lindex list index
Returns the index'th item from the list. Note: lists start from 0, not 1, so the first item is at index 0, the
second item is at index 1, and so on.
2/17/2008
Page 2 of 2
llength list
will execute the body code one time for each list item in list. On each pass, varname will
contain the value of the next list item.
In reality, the above form of foreach is the simple form, but the command is quite powerful. It will allow you to take
more than one variable at a time from the list: foreach {a b} $listofpairs { ... }. You can even take a variable at a
time from multiple lists! For example: foreach a $listOfA b $listOfB { ... }
Examples
set x "a b c"
puts "Item at index 2 of the list {$x} is: [lindex $x 2]\n"
set y [split 7/4/1776 "/"]
puts "We celebrate on the [lindex $y 1]'th day of the [lindex $y 0]'th month\n"
set z [list puts "arg 2 is $y" ]
puts "A command resembles: $z\n"
set i 0
foreach j $x {
puts "$j is item number $i in list x"
incr i
}
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
Concatenates the args into a single list. It also eliminates leading and trailing spaces in the args and adds a
single separator space between args. The args to concat may be either individual elements, or lists. If an arg is
already a list, the contents of that list is concatenated with the other args.
lappend listName ?arg1 arg2 ... argn?
Appends the args to the list listName treating each arg as a list element.
linsert listName index arg1 ?arg2 ... argn?
Returns a new list with the new list elements inserted just before the index th element of listName. Each
element argument will become a separate element of the new list. If index is less than or equal to zero, then
the new elements are inserted at the beginning of the list. If index has the value end , or if it is greater than or
equal to the number of elements in the list, then the new elements are appended to the list.
lreplace listName first last ?arg1 ... argn?
Returns a new list with N elements of listName replaced by the args. If first is less than or equal to 0, lreplace
starts replacing from the first element of the list. If first is greater than the end of the list, or the word end,
then lreplace behaves like lappend. If there are fewer args than the number of positions between first and
last, then the positions for which there are no args are deleted.
lset varName index newValue
The lset command can
Lists in Tcl are the right data structure to use when you have an arbitrary number of things, and you'd like to
access them according to their order in the list. In C, you would use an array. In Tcl, arrays are associated arrays hash tables, as you'll see in the coming sections. If you want to have a collection of things, and refer to the Nth
thing (give me the 10th element in this group of numbers), or go through them in order via foreach.
2/17/2008
Page 2 of 2
Take a look at the example code, and pay special attention to the way that sets of characters are grouped into
single list elements.
Example
set b [list a b {c d e} {f {g h}}]
puts "Treated as a list: $b\n"
set b [split "a b {c d e} {f {g h}}"]
puts "Transformed by split: $b\n"
set a [concat a b {c d e} {f {g h}}]
puts "Concated: $a\n"
lappend a {ij K lm}
puts "After lappending: $a\n"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
Lists can be searched with the lsearch command, sorted with the lsort command, and a range of list entries can be
extracted with the lrange command.
lsearch list pattern
Searches list for an entry that matches pattern, and returns the index for the first match, or a -1 if there is no
match. By default, lsearch uses "glob" patterns for matching. See the section on globbing.
lsort list
Sorts list and returns a new list in the sorted order. By default, it sorts the list into alphabetic order. Note that
this command returns the sorted list as a result, instead of sorting the list in place. If you have a list in a
variable, the way to sort it is like so: set lst [lsort $lst]
lrange list first last
Returns a list composed of the first through last entries in the list. If first is less than or equal to 0, it is
treated as the first list element. If last is end or a value greater than the number of elements in the list, it is
treated as the end. If first is greater than last then an empty list is returned.
Example
set list [list {Washington 1789} {Adams 1797} {Jefferson 1801} \
{Madison 1809} {Monroe 1817} {Adams 1825} ]
set x [lsearch $list Washington*]
set y [lsearch $list Madison*]
incr x
incr y -1
;# Set range to be not-inclusive
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
By default, lsearch uses the "globbing" method of finding a match. Globbing is the wildcarding technique that most
Unix shells use.
globbing wildcards are:
*
Matches any quantity of any character
Example
# Matches
string match f* foo
# Matches
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
Tcl commands often have "subcommands". The string command is an example of one of these. The string
command treats its first argument as a subcommand. Utilizing subcommands is a good way to make one command
do multiple things without using cryptic names. For instance, Tcl has string length instead of, say, slength.
This lesson covers these string subcommands:
string length string
Example
set string "this is my test string"
puts "There are [string length $string] characters in \"$string\""
puts "[string index $string 1] is the second character in \"$string\""
puts "\"[string range $string 5 10]\" are characters between the 5'th and 10'th"
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
There are 6 string subcommands that do pattern and string matching. These are relatively fast operations, certainly
faster than regular expressions, albeit less powerful.
string compare string1 string2
Returns the index of the character in string1 that starts the first match to string2, or -1 if there is no match.
string last string1 string2
Returns the index of the character in string1 that starts the last match to string2, or -1 if there is no match.
string wordend string index
Returns the index of the character just after the last one in the word which contains the index'th character of
string. A word is any contiguous set of letters, numbers or underscore characters, or a single other character.
string wordstart string index
Returns the index of the character just before the first one in the word which contains the index'th character of
string. A word is any contiguous set of letters, numbers or underscore characters, or a single other character.
string match pattern string
Returns 1 if the pattern matches string. The pattern is a glob style pattern.
Example
file://E:\vss\String comparisons - compare match first last wordend.htm
2/17/2008
Page 2 of 3
2/17/2008
Page 3 of 3
# Compare to "a" to determine whether the first char is upper or lower case
set comparison [string compare $name "a"]
if {$comparison >= 0} {
puts "$name starts with a lowercase letter\n"
} else {
puts "$name starts with an uppercase letter\n"
}
}
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
These are the commands which modify a string. Note that none of these modify the string in place. In all cases a
new string is returned.
string tolower string
Returns string with all the letters converted from lower to upper case.
string trim string ?trimChars?
Returns string with all occurrences of trimChars removed from both ends. By default trimChars are whitespace
(spaces, tabs, newlines). Note that the characters are not treated as a "block" of characters - in other words,
string trim "davidw" dw would return the string avi and not davi.
string trimleft string ?trimChars?
Returns string with all occurrences of trimChars removed from the left. By default trimChars are whitespace
(spaces, tabs, newlines)
string trimright string ?trimChars?
Returns string with all occurrences of trimChars removed from the right. By default trimChars are whitespace
(spaces, tabs, newlines)
format formatString ?arg1 arg2 ... argN?
Returns a string formatted in the same manner as the ANSI sprintf procedure. FormatString is a description of
the formatting to use. The full definition of this protocol is in the format man page. A useful subset of the
definition is that formatString consists of literal words, backslash sequences, and % fields. The % fields are
strings which start with a % and end with one of:
l s... Data is a string
l d... Data is a decimal integer
l x... Data is a hexadecimal integer
l o... Data is an octal integer
file://E:\vss\Modifying Strings - tolower, toupper, trim, format.htm
2/17/2008
Page 2 of 3
f...
Example
set
set
set
set
set
puts
puts
puts
puts
puts
puts
puts
puts
puts
puts
set
set
set
set
set
puts
puts
puts
puts
puts
[format
[format
[format
[format
[format
"%-20s
"%-20s
"%-20s
"%-20s
"%-20s
2/17/2008
Page 3 of 3
puts "$price4"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
Tcl also supports string operations known as regular expressions Several commands can access these methods with
a -regexp argument, see the man pages for which commands support regular expressions.
There are also two explicit commands for parsing regular expressions.
regexp ?switches? exp string ?matchVar? ?subMatch1 ... subMatchN?
Searches string for the regular expression exp. If a parameter matchVar is given, then the substring that
matches the regular expression is copied to matchVar. If subMatchN variables exist, then the parenthetical parts
of the matching string are copied to the subMatch variables, working from left to right.
regsub ?switches? exp string subSpec varName
Searches string for substrings that match the regular expression exp and replaces them with subSpec. The
resulting string is copied into varName.
Regular expressions can be expressed in just a few rules.
^
Matches the beginning of a string
$
.
*
+
[...]
2/17/2008
Page 2 of 3
2/17/2008
Page 3 of 3
matched exp. If the number following a backslash is 1-9, then that backslash sequence will be replaced by the
appropriate portion of exp that is enclosed within parentheses.
Note that the exp argument to regexp or regsub is processed by the Tcl substitution pass. Therefore quite often the
expression is enclosed in braces to prevent any special processing by Tcl.
Example
set sample "Where there is a will, There is a way."
#
# Match the first substring with lowercase letters only
#
set result [regexp {[a-z]+} $sample match]
puts "Result: $result match: $match"
#
# Match the first two words, the first one allows uppercase
set result [regexp {([A-Za-z]+) +([a-z]+)} $sample match sub1 sub2 ]
puts "Result: $result Match: $match 1: $sub1 2: $sub2"
#
# Replace a word
#
regsub "way" $sample "lawsuit" sample2
puts "New: $sample2"
#
# Use the -all option to count the number of "words"
#
puts "Number of words: [regexp -all {[^ ]} $sample]"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 7
Index
Next lesson
Regular expressions provide a very powerful method of defining a pattern, but they are a bit awkward to
understand and to use properly. So let us examine some more examples in detail.
We start with a simple yet non-trivial example: finding floating-point numbers in a line of text. Do not worry: we
will keep the problem simpler than it is in its full generality. We only consider numbers like 1.0 and not 1.00e+01.
How do we design our regular expression for this problem? By examining typical examples of the strings we want to
match:
l
Invalid numbers (that is, strings we do not want to recognise as numbers but superficially look like them):
-, +., 0.0.1, 0..2, ++1
We will accept them - because they normally are accepted and because excluding them makes our pattern
more complicated.
A pattern is beginning to emerge:
l
A number can start with a sign (- or +) or with a digit. This can be captured with the expression [-+]?, which
2/17/2008
Page 2 of 7
A number can have zero or more digits in front of a single period (.) and it can have zero or more digits
following the period. Perhaps: [0-9]*\.[0-9]* will do ...
A number may not contain a period at all. So, revise the previous expression to: [0-9]*\.?[0-9]*
instead. Or we could decide that we want to capture the digits before and after the period for special
processing:
[-+]?([0-9])*\.?([0-9]*)
3. Or, and that may be a good strategy in general!, we can carefully examine the pattern before we start actually
using it.
You see, there is a problem with the above pattern: all the parts are optional, that is, each part can match a null
string - no sign, no digits before the period, no period, no digits after the period. In other words: Our pattern can
match an empty string!
2/17/2008
Page 3 of 7
Our questionable numbers, like "+000" will be perfectly acceptable and we (grudgingly) agree. But more
surprisingly, the strings "--1" and "A1B2" will be accepted too! Why? Because the pattern can start anywhere in the
string, so it would match the substrings "-1" and "1" respectively!
We need to reconsider our pattern - it is too simple, too permissive:
l
The character before a minus or a plus, if there is any, can not be another digit, a period or a minus or plus.
Let us make it a space or a tab or the beginning of the string: ^|[ \t]
This may look a bit strange, but what it says is:
either the beginning of the string (^ outside the square brackets)
or (the vertical bar)
a space or tab (remember: the string "\t" represents the tab character).
Any sequence of digits before the period (if there is one) is allowed: [0-9]+\.?
There may be zero digits in front of the period, but then there must be at least one digit behind it: \.[0-9]+
The character after the string (if any) can not be a "+","-" or "." as that would get us into the unacceptable
number-like strings: $|[^+-.] (The dollar sign signifies the end of the string)
Before trying to write down the complete regular expression, let us see what different forms we have:
l
No period: [-+]?[0-9]+
2/17/2008
Page 4 of 7
Or:
(^|[ \t])([-+]?(\d+|\.\d+|\d+\.\d*))($|[^+-.])
The parentheses are needed to distinguish the alternatives introduced by the vertical bar and to capture the
substring we want to have. Each set of parentheses also defines a substring and this can be put into a separate
variable:
regexp {.....} $line whole char_before number nosign char_after
#
# Or simply only the recognised number (x's as placeholders, the
# last can be left out
#
regexp {.....} $line x x number
Tip: To identify these substrings: just count the opening parentheses from left to right.
If we put it to the test:
set pattern {(^|[ \t])([-+]?(\d+|\.\d+|\d+\.\d*))($|[^+-.])}
set examples {"1.0"
" .02" " +0."
"1"
"+1"
" -0.0120"
"+0000" " - "
"+."
"0001"
"0..2" "++1"
"A1.0B" "A1"}
foreach e $examples {
if { [regexp $pattern $e whole \
char_before number digits_before_period] } {
puts ">>$e<<: $number ($whole)"
} else {
puts ">>$e<<: Does not contain a valid number"
}
2/17/2008
Page 5 of 7
So our pattern correctly accepts the strings we intended to be recognised as numbers and rejects the others.
Text enclosed in a string: This is "quoted text". If we know the enclosing character in advance (double quotes,
" in this case), then "([^"])*" will capture the text inside the double quotes.
Suppose we do not know the enclosing character (it can be " or '). Then:
regexp {(["'])[^"']*\1} $string enclosed_string
You can use this technique to see if a word occurs twice in the same line of text:
2/17/2008
Page 6 of 7
(The pattern \y matches the beginning or the end of a word and \w+ indicates we want at least one
character).
l
Suppose you need to check the parentheses in some mathematical expression: (1+a)/(1-b*x) for instance. A
simple check is counting the open and close parentheses:
#
# Use the return value of [regexp] to count the number of
# parentheses ...
#
if { [regexp -all {(} $string] != [regexp -all {)} $string] } {
puts "Parentheses unbalanced!"
}
Of course, this is just a rough check. A better one is to see if at any point while scanning the string there are
more close parentheses than open parentheses. We can easily extract the parentheses and put them in a list
(the -inline option does that):
set
set
set
set
foreach p $parens {
incr balance $change($p)
if { $balance < 0 } {
puts "Parentheses unbalanced!"
}
}
2/17/2008
Page 7 of 7
if { $balance != 0 } {
puts "Parentheses unbalanced!"
}
Finally: Regular expressions are very powerful, but they have certain theoretical limitations. One of these limitations
is that they are not suitable for parsing arbitrarily nested text.
You can experiment with regular expressions using the VisualRegexp or Visual REGEXP applications.
More on the theoretical background and practical use of regular expressions (there is lots to cover!) can be found in
the book Mastering Regular Expressions by J. Friedl.
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
Searches string for the regular expression exp. If a parameter matchVar is given, then the substring that
matches the regular expression is copied to matchVar. If subMatchN variables exist, then the parenthetical parts
of the matching string are copied to the subMatch variables, working from left to right.
regsub ?switches? exp string subSpec varName
Searches string for substrings that match the regular expression exp and replaces them with subSpec. The
resulting string is copied into varName.
The regular expression (exp) in the two regular expression parsing commands is evaluated by the Tcl parser during
the Tcl substitution phase. This can provide a great deal of power, and also requires a great deal of care.
These examples show some of the trickier aspects of regular expression evaluation. The fields in each example are
discussed in painful detail in the most verbose level.
The points to remember as you read the examples are:
l
l
A left square bracket ([) has meaning to the substitution phase, and to the regular expression parser.
A set of parentheses, a plus sign, and a star have meaning to the regular expression parser, but not the Tcl
substitution phase.
A backslash sequence (\n, \t, etc) has meaning to the Tcl substitution phase, but not to the regular expression
parser.
A backslash escaped character (\[) has no special meaning to either the Tcl substitution phase or the regular
expression parser.
The phase at which a character has meaning affects how many escapes are necessary to match the character you
wish to match. An escape can be either enclosing the phrase in braces, or placing a backslash before the escaped
2/17/2008
Page 2 of 3
character.
To pass a left bracket to the regular expression parser to evaluate as a range of characters takes 1 escape. To have
the regular expression parser match a literal left bracket takes 2 escapes (one to escape the bracket in the Tcl
substitution phase, and one to escape the bracket in the regular expression parsing.). If you have the string placed
within quotes, then a backslash that you wish passed to the regular expression parser must also be escaped with a
backslash.
Note: You can copy the code and run it in tclsh or wish to see the effects.
Example
#
# Examine an overview of UNIX/Linux disks
#
set list1 [list \
{/dev/wd0a
17086
10958
5272
{/dev/wd0f
179824
127798
48428
{/dev/wd0h
1249244
967818
218962
{/dev/wd0g
98190
32836
60444
68%
73%
82%
35%
/}\
/news}\
/usr}\
/var}]
#
# Extracting a hexadecimal value ...
#
set line {Interrupt Vector?
[32(0x20)]}
regexp "\[^\t]+\t\\\[\[0-9]+\\(0x(\[0-9a-fA-F]+)\\)]" $line match hexval
puts "Hex Default is: 0x$hexval"
#
# Matching the special characters as if they were ordinary
#
2/17/2008
Page 3 of 3
Previous lesson
Index
Next lesson
2/17/2008
Associative Arrays
Page 1 of 4
Associative Arrays.
Previous lesson
Index
Next lesson
Languages like C, BASIC, FORTRAN and Java support arrays in which the index value is an integer. Tcl, like most
scripting languages (Perl, Python, PHP, etc...) supports associative arrays (also known as "hash tables") in which
the index value is a string.
The syntax for an associative array is to put the index within parentheses:
set name(first) "Mary"
set name(last) "Poppins"
puts "Full name: $name(first) $name(last)"
There are several array commands aside from simply accessing and creating arrays which will be discussed in this
and the next lesson.
array exists arrayName
Returns 1 if arrayName is an array variable. Returns 0 if arrayName is a scalar variable, proc, or does not exist.
array names arrayName ?pattern
Returns a list of the indices for the associative array arrayName. If pattern is supplied, only those indices that
match pattern are returned. The match is done using the globbing technique from string match.
array size arrayName
Returns a list in which each odd member of the list (1, 3, 5, etc) is an index into the associative array. The list
element following a name is the value of that array member.
array set arrayName dataList
Converts a list into an associative array. DataList is a list in the format of that returned by array get. Each odd
file://E:\vss\Associative Arrays.htm
2/17/2008
Associative Arrays
Page 2 of 4
member of the list (1, 3, 5, etc) is an index into the associative array, and the list element following that is the
value of that array member.
array unset arrayName ?pattern?
Unsets all of the elements in the array. If pattern exists, only the elements that match pattern are unset.
When an associative array name is given as the argument to the global command, all the elements of the
associative array become available to that proc. For this reason, Brent Welch recommends (in Practical
Programming in Tcl and Tk) using an associative array for the state structure in a package.
This method makes it simpler to share data between many procs that are working together, and doesn't pollute the
global namespace as badly as using separate globals for all shared data items.
Another common use for arrays is to store tables of data. In the example below we use an array to store a simple
database of names.
Example
proc addname {first last} {
global name
# Create a new ID (stored in the name array too for easy access)
incr name(ID)
set id $name(ID)
set name($id,first) $first
set name($id,last) $last
}
#
# Initialise the array and add a few names
#
global name
set name(ID) 0
file://E:\vss\Associative Arrays.htm
2/17/2008
Associative Arrays
addname
addname
addname
addname
Page 3 of 4
Mary Poppins
Uriah Heep
Rene Descartes
Leonardo "da Vinci"
#
# Check the contents of our database
# The parray command is a quick way to
# print it
#
parray name
#
# Some array commands
#
array set array1 [list {123}
{234}
{345}
{456}
{Abigail Aardvark} \
{Bob Baboon} \
{Cathy Coyote} \
{Daniel Dog} ]
file://E:\vss\Associative Arrays.htm
2/17/2008
Associative Arrays
Page 4 of 4
} else {
puts "$variable Does Not Exist"
}
}
puts "\ntesting unsetting a member of an array"
existence a(0)
puts "a0 has been unset"
unset a(0)
existence a(0)
puts "\ntesting unsetting several members of an array, with an error"
existence a(3)
existence a(4)
catch {unset a(3) a(0) a(4)}
puts "\nAfter attempting to delete a(3), a(0) and a(4)"
existence a(3)
existence a(4)
puts "\nUnset all the array's elements"
existence a
array unset a *
puts "\ntesting unsetting an array"
existence a
puts "a has been unset"
unset a
existence a
Previous lesson
file://E:\vss\Associative Arrays.htm
Index
Next lesson
2/17/2008
Page 1 of 4
Index
Next lesson
Often you will want to loop through the contents of an associative array - without having to specify the elements
explicitly. For this the array names and array get commands are very useful. With both you can give a (glob-style)
pattern to select what elements you need:
foreach name [array names mydata] {
puts "Data on \"$name\": $mydata($name)"
}
#
# Get names and values directly
#
foreach {name value} [array get mydata] {
puts "Data on \"$name\": $value"
}
Note, however, that the elements will not be returned in any predictable order: this has to do with the underlying
"hash table". If you want a particular ordering (alphabetical for instance), use code like:
foreach name [lsort [array names mydata]] {
puts "Data on \"$name\": $mydata($name)"
}
While arrays are great as a storage facility for some purposes, they are a bit tricky when you pass them to a
procedure: they are actually collections of variables. This will not work:
proc print12 {a} {
puts "$a(1), $a(2)"
}
2/17/2008
Page 2 of 4
The reason is very simple: an array does not have a value. Instead the above code should be:
proc print12 {array} {
upvar $array a
puts "$a(1), $a(2)"
}
set array(1) "A"
set array(2) "B"
print12 array
So, instead of passing a "value" for the array, you pass the name. This gets aliased (via the upvar command) to a
local variable (that behaves the as original array). You can make changes to the original array in this way too.
Example
#
# The example of the previous lesson revisited - to get a
# more general "database"
#
proc addname {db first last} {
upvar $db name
# Create a new ID (stored in the name array too for easy access)
incr name(ID)
set id $name(ID)
set name($id,first) $first
set name($id,last) $last
2/17/2008
Page 3 of 4
}
proc report {db} {
upvar $db name
# Loop over the last names: make a map from last name to ID
foreach n [array names name "*,last"] {
#
# Split the name to get the ID - the first part of the name!
#
regexp {^[^,]+} $n id
#
# Store in a temporary array:
# an "inverse" map of last name to ID)
#
set last
$name($n)
set tmp($last) $id
}
#
# Now we can easily print the names in the order we want!
#
foreach last [lsort [array names tmp]] {
set id $tmp($last)
puts "
$name($id,first) $name($id,last)"
}
}
#
# Initialise the array and add a few names
#
set fictional_name(ID) 0
set historical_name(ID) 0
addname fictional_name Mary Poppins
addname fictional_name Uriah Heep
addname fictional_name Frodo Baggins
addname historical_name Rene Descartes
addname historical_name Richard Lionheart
2/17/2008
Page 4 of 4
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 5
Index
Next lesson
They are actually collections of variables and as such can not contain other arrays.
Passing them to a procedure means that you have to pass the name and use the upvar command to actually
use them inside the procedure.
Multidimensional arrays (that is, arrays whose index consists of two or more parts) have to be emulated with
constructions like:
set array(1,2) 10
set array(2,2) 11
This is quite possible, but it can become very clumsy (there can be no intervening spaces for instance).
In Tcl 8.5 the dict command has been introduced. This provides efficient access to key-value pairs, just like arrays,
but dictionaries are pure values. This means that you can pass them to a procedure just as a list or a string,
without the need for dict.
Unlike arrays, you can nest dictionaries, so that the value for a particular key consists of another dictionary. That
way you can elegantly build complicated data structures, such as hierarchical databases.
Here is an example (adapted from the man page):
#
# Create a dictionary:
# Two clients, known by their client number,
# with forenames, surname
#
2/17/2008
set
set
set
set
clients
clients
clients
clients
1
1
2
2
forenames
surname
forenames
surname
Page 2 of 5
Joe
Schmoe
Anne
Other
#
# Print a table
#
puts "Number of clients: [dict size $clients]"
dict for {id info} $clients {
puts "Client $id:"
dict with info {
puts "
Name: $forenames $surname"
}
}
l
l
We fill a dictionary, called clients, with the information we have on two clients. The dictionary has two keys,
"1" and "2" and the value for these keys is an dictionary - again with two keys "forenames" and "surname".
This is because the dict set command accepts a list of keywords (descending into the nesting of the
dictionaries) and uses the last argument as the actual value.
Then we use a kind of foreach to loop over the contents of the dictionary (only the first level!).
To get at the actual values in the dictionary that is stored with the client IDs we use the dict with command.
This command takes the dictionary and sets variables by the name of the keys to the values in that dictionary.
That way the contents is readily available via these variables.
2/17/2008
Page 3 of 5
While arrays are great as a storage facility for some purposes, they are a bit tricky when you pass them to a
procedure: they are actually collections of variables. This will not work:
proc print12 {a} {
puts "$a(1), $a(2)"
}
set array(1) "A"
set array(2) "B"
print12 $array
The reason is very simple: an array does not have a value. Instead the above code should be:
proc print12 {array} {
upvar $array a
puts "$a(1), $a(2)"
}
set array(1) "A"
set array(2) "B"
print12 array
So, instead of passing a "value" for the array, you pass the name. This gets aliased (via the upvar command) to a
local variable (that behaves the as original array). You can make changes to the original array in this way too.
Example
#
# The example of the previous lesson revisited - to get a
# more general "database"
#
2/17/2008
Page 4 of 5
}
proc report {db} {
upvar $db name
# Loop over the last names: make a map from last name to ID
foreach n [array names name "*,last"] {
#
# Split the name to get the ID - the first part of the name!
#
regexp {^[^,]+} $n id
#
# Store in a temporary array:
# an "inverse" map of last name to ID)
#
set last
$name($n)
set tmp($last) $id
}
#
# Now we can easily print the names in the order we want!
#
foreach last [lsort [array names tmp]] {
set id $tmp($last)
puts "
$name($id,first) $name($id,last)"
}
}
2/17/2008
Page 5 of 5
#
# Initialise the array and add a few names
#
set fictional_name(ID) 0
set historical_name(ID) 0
addname fictional_name Mary Poppins
addname fictional_name Uriah Heep
addname fictional_name Frodo Baggins
addname
addname
addname
addname
addname
historical_name
historical_name
historical_name
historical_name
historical_name
Rene Descartes
Richard Lionheart
Leonardo "da Vinci"
Charles Baudelaire
Julius Caesar
#
# Some simple reporting
#
puts "Fictional characters:"
report fictional_name
puts "Historical characters:"
report historical_name
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 4
Index
Next lesson
Tcl provides several methods to read from and write to files on disk. The simplest methods to access a file are via
gets and puts. When there is a lot of data to be read, however, it is sometimes more efficient to use the read
command to load an entire file, and then parse the file into lines with the split command.
These methods can also be used for communicating over sockets and pipes. It is even possible, via the so-called
virtual file system to use files stored in memory rather than on disk. Tcl provides an almost uniform interface to
these very different resources, so that in general you do not need to concern yourself with the details.
open fileName ?access? ?permission?
Opens a file and returns a token to be used when accessing the file via gets, puts, close, etc.
l FileName is the name of the file to open.
l access is the file access mode
r......Open the file for reading. The file must already exist.
r+...Open the file for reading and writing. The file must already exist.
w.....Open the file for writing. Create the file if it doesn't exist, or set the length to zero if it does
exist.
w+..Open the file for reading and writing. Create the file if it doesn't exist, or set the length to zero if
it does exist.
a......Open the file for writing. The file must already exist. Set the current location to the end of the
file.
a+...Open the file for writing. The file does not exist, create it. Set the current location to the end of
the file.
l permission is an integer to use to set the file access permissions. The default is rw-rw-rw- (0666). You can
use it to set the permissions for the file's owner, the group he/she belongs to and for all the other users.
For many applications, the default is fine.
close fileID
2/17/2008
Page 2 of 4
Closes a file previously opened with open, and flushes any remaining output.
gets fileID ?varName?
Reads a line of input from FileID, and discards the terminating newline.
If there is a varName argument, gets returns the number of characters read (or -1 if an EOF occurs), and places
the line of input in varName.
If varName is not specified, gets returns the line of input. An empty string will be returned if:
l
l
Writes the characters in string to the stream referenced by fileID, where fileID is one of:
l The value returned by a previous call to open with write access.
l
stdout
stderr
Reads all the remaining bytes from fileID, and returns that string. If -nonewline is set, then the last character
will be discarded if it is a newline. Any existing end of file condition is cleared before the read command is
executed.
read fileID numBytes
Reads up to numBytes from fileID, and returns the input as a Tcl string. Any existing end of file condition is
cleared before the read command is executed.
seek fileID offset ?origin?
Change the current position within the file referenced by fileID. Note that if the file was opened with "a" access
that the current position can not be set before the end of the file for writing, but can be set to the beginning of
the file for reading.
l fileID is one of:
a File identifier returned by open
stdin
stdout
2/17/2008
Page 3 of 4
stderr
is the offset in bytes at which the current position is to be set. The position from which the offset is
measured defaults to the start of the file, but can be from the current location, or the end by setting
origin appropriately.
origin is the position to measure offset from. It defaults to the start of the file. Origin must be one of:
start.........Offset is measured from the start of the file.
current...Offset is measured from the current position in the file.
end...........Offset is measured from the end of the file.
offset
tell fileID
The file I/O is buffered. The output may not be sent out when you expect it to be sent. Files will all be closed
and flushed when your program exits normally, but may only be closed (not flushed) if the program is
terminated in an unexpected manner.
There are a finite number of open file slots available. If you expect the program to run in a manner that will
cause it to open several files, remember to close the files when you are done with them.
An empty line is indistinguishable from an EOF with the command:
set string [gets filename]
l
l
l
Use the eof command to determine if the file is at the end or use the other form of gets (see the example).
You can't overwrite any data in a file that was opened with a (append) access. You can, however seek to the
beginning of the file for gets commands.
Opening a file with the w+ access will allow you to overwrite data, but will delete all existing data in the file.
Opening a file with the r+ access will allow you to overwrite data, while saving the existing data in the file.
By default the commands assume that strings represent "readable" text. If you want to read "binary" data,
you will have to use the fconfigure command.
2/17/2008
Page 4 of 4
Often, especially if you deal with configuration data for your programs, you can use the source command
instead of the relatively low-level commands presented here. Just make sure your data can be interpreted as
Tcl commands and "source" the file.
Example
#
# Count the number of lines in a text file
#
set infile [open "myfile.txt" r]
set number 0
#
# gets with two arguments returns the length of the line,
# -1 if the end of the file is found
#
while { [gets $infile line] >= 0 } {
incr number
}
close $infile
puts "Number of lines: $number"
#
# Also report it in an external file
#
set outfile [open "report.out" w]
puts $outfile "Number of lines: $number"
close $outfile
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 7
Index
Next lesson
There are two commands that provide information about the file system, glob and file.
provides the access to the names of files in a directory. It uses a name matching mechanism similar to the
UNIX ls command or the Windows (DOS) dir command, to return a list of names that match a pattern.
glob
file
l
2/17/2008
Page 2 of 7
size
Between these two commands, a program can obtain most of the information that it may need and manipulate the
files and directories.
While retrieving information about what files are present and what properties they have is usually a highly
platform-dependent matter, Tcl provides an interface that hides almost all details that are specific to the platform
(but are irrelevant to the programmer).
To take advantage of this feature, always manipulate file names via the file join, file split commands and the
others in the first category.
For instance to refer to a file in a directory upwards of the current one:
set upfile [file join ".." "myfile.out"]
# upfile will have the value "../myfile.out"
2/17/2008
Page 3 of 7
Retrieving all the files with extension ".tcl" in the current directory:
set tclfiles [glob *.tcl]
puts "Name - date of last modification"
foreach f $tclfiles {
puts "$f - [clock format [file mtime $f] -format %x]"
}
(The clock command turns the number of seconds returned by the file mtime command into a simple date string,
like "12/22/04")
glob ?switches? pattern ?patternN?
-nocomplain
Allows glob to return an empty list without causing an error. Without this flag, an error would be
generated when the empty list was returned.
-types typeList
Selects which type of files/directory the command should return. The typeList may consist of type letters,
like a "d" for directories and "f" for ordinary files as well as letters and keywords indicating the user's
permissions ("r" for files/directories that can be read for instance).
--
Marks the end of switches. This allows the use of "-" in a pattern without confusing the glob parser.
follows the same matching rules as the string match globbing rules with these exceptions:
{a,b,...} Matches any of the strings a,b, etc.
A "." at the beginning of a filename must match a "." in the filename. The "." is only a wildcard if it is not
the first character in a name.
pattern
l
l
2/17/2008
Page 4 of 7
Note that the filenames that match pattern are returned in an arbitrary order (that is, do not expect them to
be sorted in alphabetical order, for instance).
file atime name
Returns the number of seconds since some system-dependent start date, also known as the
"epoch" (frequently 1/1/1970) when the file name was last accessed. Generates an error if the file doesn't exist,
or the access time cannot be queried.
file copy ?-force? name target
Copy the file/directory name to a new file target (or to an existing directory with that name)
The switch -force allows you to overwrite existing files.
file delete ?-force? name
Returns the directory portion of a path/filename string. If name contains no slashes, file dirname returns a ".". If
the last "/" in name is also the first character, it returns a "/".
file executable name
Returns 1 if the file name exists, and the user has search access in all the directories leading to the file.
Otherwise, 0 is returned.
file extension name
2/17/2008
Page 5 of 7
This returns the same information returned by the system call lstat. The results are placed in the associative
array varName. The indexes in varName are:
l atime.......time of last access
l ctime.......time of last file status change
l dev...........inode's device
l gid............group ID of the file's group
l ino............inode's number
l mode.......inode protection mode
l mtime.....time of last data modification
l nlink........number of hard links
l size...........file size, in bytes
l type..........Type of File
l uid.............user ID of the file's owner
Because this calls lstat, if name is a symbolic link, the values in varName will refer to the link, not the file that is
linked to. (See also the stat subcommand)
file mkdir name
Returns the time of the last modification in seconds since Jan 1, 1970 or whatever start date the system uses.
file owned name
Returns the name of the file a symlink is pointing to. If name isn't a symlink, or can't be read, an error is
generated.
file rename ?-force? name target
Rename file/directory name to the new name target (or to an existing directory with that name)
The switch -force allows you to overwrite existing files.
file rootname name
2/17/2008
Page 6 of 7
Returns all the characters in name up to but not including the last ".". Returns $name if name doesn't include a ".".
file size name
This returns the same information returned by the system call stat. The results are placed in the associative
array varName. The indexes in varName are:
l atime.......time of last access
l ctime.......time of last file status change
l dev...........inode's device
l gid............group ID of the file's group
l ino............inode's number
l mode.......inode protection mode
l mtime.....time of last data modification
l nlink........number of hard links
l size...........file size in bytes
l type..........Type of file
l uid.............user ID of the file's owner
file tail name
Returns all of the characters in name after the last slash. Returns the name if name contains no slashes.
file type name
Returns a string giving the type of file name, which will be one of:
l file...................................Normal file
l directory........................Directory
l characterSpecial.......Character oriented device
l blockSpecial.............. Block oriented device
l fifo...................................Named pipe
l link..................................Symbolic link
l socket...........................Named socket
file writable name
2/17/2008
Page 7 of 7
Example
#
# Report all the files and subdirectories in the current directory
# For files: show the size
# For directories: show that they _are_ directories
#
set dirs [glob -nocomplain -type d *]
if { $dirs != {} } {
puts "Directories:
foreach d [lsort $dirs] {
puts "
$d"
}
} else {
puts "(no subdirectories)"
}
set files [glob -nocomplain -type f *]
if { $files != {} } {
puts "Files:
foreach f [lsort $files] {
puts "
[file size $f] - $f"
}
} else {
puts "(no files)"
}
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 1
Index
Next lesson
TODO: lesson on socket and fileevent (or "chan event" in 8.5). But see also Lesson 40: socket, fileevent, vwait.
Example
#
# A little echo server
#
proc accept {socket address port} {
puts "Accepted connection $socket from $address\:$port"
# Copy input from the socket directly back to the socket
chan copy $socket $socket -command [list finish $socket]
}
proc finish {socket args} {
puts "Closed $socket"
catch { chan close $socket }
}
socket -server accept 8080
# Start the event loop by waiting on a non-existant variable 'forever'
vwait forever
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 7
Index
Next lesson
So far the lessons have dealt with programming within the Tcl interpreter. However, Tcl is also useful as a scripting
language to tie other packages or programs together. To accomplish this function, Tcl has two ways to start
another program:
l
open
exec
The open call is the same call that is used to open a file. If the first character in the file name argument is a "pipe"
symbol (|), then open will treat the rest of the argument as a program name, and will run that program with the
standard input or output connected to a file descriptor. This "pipe" connection can be used to read the output from
that other program or to write fresh input data to it or both.
If the "pipe" is opened for both reading and writing you must be aware that the pipes are buffered. The output from
a puts command will be saved in an I/O buffer until the buffer is full, or until you execute a flush command to force
it to be transmitted to the other program. The output of this other program will not be available to a read or gets
until its output buffer is filled up or flushed explicitly.
(Note: as this is internal to this other program, there is no way that your Tcl script can influence that. The other
program simply must cooperate. Well, that is not entirely true: the expect extension actually works around this
limitation by exploiting deep system features.)
The exec call is similar to invoking a program (or a set of programs piped together) from the prompt in an
interactive shell or a DOS-box or in a UNIX/Linux shell script. It supports several styles of output redirection, or it
can return the output of the other program(s) as the return value of the exec call.
open |progName ?access?
2/17/2008
Page 2 of 7
Returns a file descriptor for the pipe. The progName argument must start with the pipe symbol. If progName is
enclosed in quotes or braces, it can include arguments to the subprocess.
exec ?switches? arg1 ?arg2? ... ?argN?
exec treats its arguments as the names and arguments for a set of programs to run. If the first args start with
a "-", then they are treated as switches to the exec command, instead of being invoked as subprocesses or
subprocess options.
switches
are:
-keepnewline
Retains a trailing newline in the pipeline's output. Normally a trailing newline will be deleted.
--
Marks the end of the switches. The next string will be treated as arg1, even if it starts with a "-"
arg1
l
l
l
l
will start the program myprog in the background, and return immediately. There is no connection between
that program and the Tcl script, both can run on independently.
The & must be the last argument - you can use all other types of arguments in front of it.
[NOTE: add information on how to wait for the program to finish?]
There are many I/O redirection commands. The main subset of these commands is:
|
Pipes the standard output of the command preceding the pipe symbol into the standard input of the
command following the pipe symbol.
< fileName
The first program in the pipe will read input from fileName.
file://E:\vss\Running other programs from Tcl - exec, open.htm
2/17/2008
Page 3 of 7
<@ fileID
The first program in the pipe will read input from the Tcl descriptor fileID. fileID is the value returned
from an open ... "r" command.
<< value
The first program in the pipe will read value as its input.
> fileName
The output of the last program in the pipe will be sent to fileName. Any previous contents of fileName will
be lost.
>> fileName
The output of the last program in the pipe will be appended to fileName.
2> fileName
The standard error from all the programs in the pipe will be sent to fileName. Any previous contents of
fileName will be lost.
2>> fileName
The standard error from all the programs in the pipe will be appended to fileName.
>@ fileID
The output from the last program in the pipe will be written to fileID. fileID is the value returned from an
open ... "w" command.
If you are familiar with shell programming, there are a few differences to be aware of when you are writing Tcl
scripts that use the exec and open calls.
l
You don't need the quotes that you would put around arguments to escape them from the shell expanding
them. In the example, the argument to the sed command is not put in quotes. If it were put in quotes, the
quotes would be passed to sed, instead of being stripped off (as the shell does), and sed would report an error.
If you use the open |cmd "r+" construct, you must follow each puts with a flush to force Tcl to send the
command from its buffer to the program. The output from the program itself may be buffered in its output
buffer.
You can sometimes force the output from the external program to flush by sending an exit command to the
process.
2/17/2008
Page 4 of 7
You can also use the fconfigure command to make a connection (channel) unbuffered.
As already mentioned, expect extension to Tcl provides a much better interface to other programs, which in
particular handles the buffering problem.
[NOTE: add good reference to expect]
l
If one of the commands in an open |cmd fails the open does not return an error. However, attempting to read
input from the file descriptor with gets $file will return an empty string. Using the gets $file input construct
will return a character count of -1.
Tcl does not expand file names like the UNIX/Linux shells do. So:
exec ls *.tcl
will fail - there is most probably no file with the literal name "*.tcl".
If you need such an expansion, you should use the glob command:
eval exec ls [glob *.tcl]
where the {expand} prefix is used to force the list to become individual arguments.
If one of the commands in an exec call fails to execute, the exec will return an error, and the error output will
include the last line describing the error.
The exec treats any output to standard error to be an indication that the external program failed. This is simply
a conservative assumption: many programs behave that way and they are sloppy in setting return codes.
Some programs however write to standard error without intending this as an indication of an error. You can
guard against this from upsetting your script by using the catch command:
2/17/2008
Page 5 of 7
To inspect the return code from a program and the possible reason for failure, you can use the global errorInfo
variable:
if { [catch { exec ls *.tcl } msg] } {
puts "Something seems to have gone wrong:"
puts "Information about it: $::errorInfo"
}
Example
#
# Write a Tcl script to get a platform-independent program:
#
# Create a unique (mostly) file name for a Tcl program
set TMPDIR "/tmp"
if { [info exists ::env(TMP)] } {
set TMPDIR $::env(TMP)
}
set tempFileName "$TMPDIR/invert_[pid].tcl"
# Open the output file, and
# write the program to it
set outfl [open $tempFileName w]
puts $outfl {
set len [gets stdin line]
if {$len < 5} {exit -1}
for {set i [expr {$len-1}]} {$i >= 0} {incr i -1} {
2/17/2008
Page 6 of 7
2/17/2008
Page 7 of 7
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
Tcl provides a number of commands for introspection - commands that tell what is going on in your program, what
the implementation is of your procedures, which variables have been set and so on.
The info command allows a Tcl program to obtain information from the Tcl interpreter. The next three lessons cover
aspects of the info command. (Other commands allowing introspection involve: traces, namespaces, commands
scheduled for later execution via the after command and so on.)
This lesson covers the info subcommands that return information about which procs, variables, or commands are
currently in existence in this instance of the interpreter. By using these subcommands you can determine if a
variable or proc exists before you try to access it.
The code below shows how to use the info exists command to make an incr that will never return a no such
variable error, since it checks to be certain that the variable exists before incrementing it:
proc safeIncr {val {amount 1}} {
upvar $val v
if { [info exists v] } {
incr v $amount
} else {
set v $amount
}
}
2/17/2008
Page 2 of 3
Returns a list of the commands, both internal commands and procedures, whose names match pattern.
info exists varName
Returns 1 if varName exists as a variable (or an array element) in the current context, otherwise returns 0.
info functions ?pattern?
Returns a list of the mathematical functions available via the expr command that match pattern .
info globals ?pattern?
Returns a list of the local and global variables that match pattern.
Example
if {[info procs safeIncr] eq "safeIncr"} {
safeIncr a
}
puts "After calling SafeIncr with a non existent variable: $a"
set a 100
safeIncr a
puts "After calling SafeIncr with a variable with a value of 100: $a"
safeIncr b -3
puts "After calling safeIncr with a non existent variable by -3: $b"
set b 100
safeIncr b -3
puts "After calling safeIncr with a variable whose value is 100 by -3: $b"
2/17/2008
Page 3 of 3
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
There are a number of subcommands to the info command that provide information about the current state of the
interpreter. These commands provide access to information like the current version and patchlevel, what script is
currently being executed, how many commands have been executed, or how far down in the call tree the current
proc is executing.
The info tclversion and info patchlevel can be used to find out if the revision level of the interpreter running your
code has the support for features you are using. If you know that certain features are not available in certain
revisions of the interpreter, you can define your own procs to handle this, or just exit the program with an error
message.
The info cmdcount and info level can be used while optimizing a Tcl script to find out how many levels and
commands were necessary to accomplish a function.
Note that the pid command is not part of the info command, but a command in its own right.
Subcommands that return information about the current state of the interpreter
(Note: There are several other subcommands that can be useful at times)
info cmdcount
Returns the total number of commands that have been executed by this interpreter.
info level ?number?
Returns the stack level at which the compiler is currently evaluating code. 0 is the top level, 1 is a proc called
from top, 2 is a proc called from a proc, etc.
If number is a positive value, info level returns a the name and arguments of the proc at that level on the
2/17/2008
Page 2 of 3
stack. Number is that same value that info level would return if it were called in the proc being referenced.
If number number is a negative value, it refers to the current level plus number. Thus, info level returns a the
name and arguments of the proc at that level on the stack.
info patchlevel
Returns the value of the global variable tcl_patchlevel. This is a three-levels version number identifying the Tcl
version, like: "8.4.6"
info script
Returns the name of the file currently being evaluated, if one is being evaluated. If there is no file being
evaluated, returns an empty string.
This can be used for instance to determine the directory holding other scripts or files of interest (they often
live in the same directory or in a related directory), without having to hardcode the paths.
info tclversion
Returns the value of the global variable tcl_version. This is the revision number of this interpreter, like: "8.4".
pid
Example
puts "This is how many commands have been executed: [info cmdcount]"
puts "Now *THIS* many commands have been executed: [info cmdcount]"
puts "\nThis interpreter is revision level: [info tclversion]"
puts "This interpreter is at patch level: [info patchlevel]"
puts "The process id for this program is [pid]"
proc factorial {val} {
puts "Current level: [info level] - val: $val"
set lvl [info level]
2/17/2008
Page 3 of 3
if {$lvl == $val} {
return $val
}
return [expr {($val-$lvl) * [factorial $val]]}]
}
set count1 [info cmdcount]
set fact [factorial 3]
set count2 [info cmdcount]
puts "The factorial of 3 is $fact"
puts "Before calling the factorial proc, $count1 commands had been executed"
puts "After calling the factorial proc, $count2 commands had been executed"
puts "It took [expr $count2-$count1] commands to calculate this factorial"
#
# Use [info script] to determine where the other files of interest
# reside
#
set sysdir [file root [info script]]
source [file join $sysdir "utils.tcl"]
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
The info command includes a set of subcommands that will provide all the info you could want about a proc. These
subcommands will return the body of a proc, the arguments to the proc, and the value of any default arguments.
These subcommands can be used to:
l
l
l
Returns 1 if the argument arg in procedure procName has a default, and sets varName to the default. Otherwise,
returns 0.
Example
proc demo {argument1 {default "DefaultValue"} } {
puts "This is a demo proc. It is being called with $argument1 and $default"
#
# We can use [info level] to find out if a value was given for
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Modularization - source
Page 1 of 2
Modularization - source
Previous lesson
Index
Next lesson
The source command will load a file and execute it. This allows a program to be broken up into multiple files, with
each file defining procedures and variables for a particular area of functionality. For instance, you might have a file
called database.tcl that contains all the procedures for dealing with a database, or a file called gui.tcl that handles
creating a graphical user interface with Tk. The main script can then simply include each file using the source
command. More powerful techniques for program modularization are discussed in the next lesson on packages.
This command can be used to:
l
l
l
l
source fileName
Reads the script in fileName and executes it. If the script executes successfully, source returns the value of the
last statement in the script.
If there is an error in the script, source will return that error.
If there is a return (other than within a proc definition) then source will return immediately, without executing
the remainder of the script.
If fileName starts with a tilde (~) then $env(HOME) will substituted for the tilde, as is done in the file command.
Example
sourcedata.tcl:
file://E:\vss\Modularization - source.htm
2/17/2008
Modularization - source
Page 2 of 2
sourcemain.tcl:
set filename "sourcedata.tcl"
puts "Global variables visible before sourcing $filename:"
puts "[lsort [info globals]]\n"
if {[info procs testproc] eq ""} {
puts "testproc does not exist.
source $filename
}
sourcing $filename"
Previous lesson
file://E:\vss\Modularization - source.htm
Index
Next lesson
2/17/2008
Page 1 of 8
Index
Next lesson
The previous lesson showed how the source command can be used to separate a program into multiple files, each
responsible for a different area of functionality. This is a simple and useful technique for achieving modularity.
However, there are a number of drawbacks to using the source command directly. Tcl provides a more powerful
mechanism for handling reusable units of code called packages. A package is simply a bundle of files implementing
some functionality, along with a name that identifies the package, and a version number that allows multiple
versions of the same package to be present. A package can be a collection of Tcl scripts, or a binary library, or a
combination of both. Binary libraries are not discussed in this tutorial.
Using packages
The package command provides the ability to use a package, compare package versions, and to register your own
packages with an interpreter. A package is loaded by using the package require command and providing the package
name and optionally a version number. The first time a script requires a package Tcl builds up a database of available
packages and versions. It does this by searching for package index files in all of the directories listed in the
tcl_pkgPath and auto_path global variables, as well as any subdirectories of those directories. Each package provides
a file called pkgIndex.tcl that tells Tcl the names and versions of any packages in that directory, and how to load
them if they are needed.
It is good style to start every script you create with a set of package require statements to load any packages
required. This serves two purposes: making sure that any missing requirements are identified as soon as possible;
and, clearly documenting the dependencies that your code has. Tcl and Tk are both made available as packages
and it is a good idea to explicitly require them in your scripts even if they are already loaded as this makes your
scripts more portable and documents the version requirements of your script.
Creating a package
2/17/2008
Page 2 of 8
The first step is to add a package provide statement to your script. It is good style to place this statement at the top
of your script. The package provide command tells Tcl the name of your package and the version being provided.
The next step is to create a pkgIndex.tcl file. This file tells Tcl how to load your package. In essence the index file is
simply a Tcl file which is loaded into the interpreter when Tcl searches for packages. It should use the package
ifneeded command register a script which will load the package when it is required. The pkgIndex.tcl file is evaluated
globally in the interpreter when Tcl first searches for any package. For this reason it is very bad style for an index
script to do anything other than tell Tcl how to load a package; index scripts should not define procs, require
packages, or perform any other action which may affect the state of the interpreter.
The simplest way to create a pkgIndex.tcl script is to use the pkg_mkIndex command. The pkg_mkIndex command scans
files which match a given pattern in a directory looking for package provide commands. From this information it
generates an appropriate pkgIndex.tcl file in the directory.
Once a package index has been created, the next step is to move the package to somewhere that Tcl can find it.
The tcl_pkgPath and auto_path global variables contain a list of directories that Tcl searches for packages. The
package index and all the files that implement the package should be installed into a subdirectory of one of these
directories. Alternatively, the auto_path variable can be extended at run-time to tell Tcl of new places to look for
packages.
package require ?-exact? name ?version?
Loads the package identified by name. If the -exact switch is given along with a version number then only that
exact package version will be accepted. If a version number is given, without the -exact switch then any
version equal to or greater than that version (but with the same major version number) will be accepted. If no
version is specified then any version will be loaded. If a matching package can be found then it is loaded and
the command returns the actual version number; otherwise it generates an error.
2/17/2008
Page 3 of 8
If a version is given this command tells Tcl that this version of the package indicated by name is loaded. If a
different version of the same package has already been loaded then an error is generated. If the version
argument is omitted, then the command returns the version number that is currently loaded, or the empty
string if the package has not been loaded.
pkg_mkIndex ?-direct? ?-lazy? ?-load pkgPat? ?-verbose? dir ?pattern pattern ...?
Creates a pkgIndex.tcl file for a package or set of packages. The command
Namespaces
One problem that can occur when using packages, and particularly when using code written by others is that of
name collision. This happens when two pieces of code try to define a procedure or variable with the same name. In
Tcl when this occurs the old procedure or variable is simply overwritten. This is sometimes a useful feature, but
more often it is the cause of bugs if the two definitions are not compatible. To solve this problem, Tcl provides a
namespace command to allow commands and variables to be partitioned into separate areas, called namespaces.
Each namespace can contain commands and variables which are local to that namespace and cannot be overwritten
by commands or variables in other namespaces. When a command in a namespace is invoked it can see all the
other commands and variables in its namespace, as well as those in the global namespace. Namespaces can also
contain other namespaces. This allows a hierarchy of namespaces to be created in a similar way to a file system
hierarchy, or the Tk widget hierarchy. Each namespace itself has a name which is visible in its parent namespace.
Items in a namespace can be accessed by creating a path to the item. This is done by joining the names of the
items with ::. For instance, to access the variable bar in the namespace foo, you could use the path foo::bar. This
kind of path is called a relative path because Tcl will try to follow the path relative to the current namespace. If that
fails, and the path represents a command, then Tcl will also look relative to the global namespace. You can make a
path fully-qualified by describing its exact position in the hierachy from the global namespace, which is named ::.
For instance, if our foo namespace was a child of the global namespace, then the fully-qualified name of bar would
be ::foo::bar. It is usually a good idea to use fully-qualified names when referring to any item outside of the
current namespace to avoid surprises.
A namespace can export some or all of the command names it contains. These commands can then be imported
2/17/2008
Page 4 of 8
into another namespace. This in effect creates a local command in the new namespace which when invoked calls
the original command in the original namespace. This is a useful technique for creating short-cuts to frequently
used commands from other namespaces. In general, a namespace should be careful about exporting commands
with the same name as any built-in Tcl command or with a commonly used name.
Some of the most important commands to use when dealing with namespaces are:
namespace eval path script
This command evaluates the script in the namespace specified by path. If the namespace doesn't exist then it
is created. The namespace becomes the current namespace while the script is executing, and any unqualified
names will be resolved relative to that namespace. Returns the result of the last command in script.
namespace delete ?namespace namespace ...?
Deletes each namespace specified, along with all variables, commands and child namespaces it contains.
namespace current
Adds any commands matching one of the patterns to the list of commands exported by the current
namespace. If the -clear switch is given then the export list is cleared before adding any new commands. If no
arguments are given, returns the currently exported command names. Each pattern is a glob-style pattern
such as *, [a-z]*, or *foo*.
namespace import ?-force? ?pattern pattern ...?
Imports all commands matching any of the patterns into the current namespace. Each pattern is a glob-style
pattern such as foo::*, or foo::bar.
2/17/2008
Page 5 of 8
Example
This example creates a package which provides a stack data structure.
# Register the package
package provide tutstack 1.0
package require Tcl
8.5
# Create the namespace
namespace eval ::tutstack {
# Export commands
namespace export create destroy push pop peek empty
# Set up state
variable stack
variable id 0
}
# Create a new stack
proc ::tutstack::create {} {
variable stack
variable id
set token "stack[incr id]"
set stack($token) [list]
return $token
}
# Destroy a stack
proc ::tutstack::destroy {token} {
variable stack
unset stack($token)
}
# Push an element onto a stack
proc ::tutstack::push {token elem} {
variable stack
lappend stack($token) $elem
2/17/2008
Page 6 of 8
}
# Check if stack is empty
proc ::tutstack::empty {token} {
variable stack
set num [llength $stack($token)]
return [expr {$num == 0}]
}
# See what is on top of the stack without removing it
proc ::tutstack::peek {token} {
variable stack
if {[empty $token]} {
error "stack empty"
}
return [lindex $stack($token) end]
}
# Remove an element from the top of the stack
proc ::tutstack::pop {token} {
variable stack
set ret [peek $token]
set stack($token) [lrange $stack($token) 0 end-1]
return $ret
}
2/17/2008
Page 7 of 8
tutstack::destroy $stack
Ensembles
A common way of structuring related commands is to group them together into a single command with subcommands. This type of command is called an ensemble command, and there are many examples in the Tcl
standard library. For instance, the string command is an ensemble whose sub-commands are length, index, match
etc. Tcl 8.5 introduced a handy way of converting a namespace into an ensemble with the namespace ensemble
command. This command is very flexible, with many options to specify exactly how sub-commands are mapped to
commands within the namespace. The most basic usage is very straightforward, however, and simply creates an
ensemble command with the same name as the namespace and with all exported procedures registered as subcommands. To illustrate this, we will convert our stack data structure into an ensemble:
package require tutstack 1.0
package require Tcl
8.5
namespace eval ::tutstack {
# Create the ensemble command
namespace ensemble create
}
# Now we can use our stack through the ensemble command
set stack [tutstack create]
foreach num {1 2 3 4 5} { tutstack push $stack $num }
while { ![tutstack empty $stack] } {
puts "[tutstack pop $stack]"
}
tutstack destroy $stack
As well as providing a nicer syntax for accessing functionality in a namespace, ensemble commands also help to
clearly distinguish the public interface of a package from the private implementation details, as only exported
commands will be registered as sub-commands and the ensemble will enforce this distinction. Readers who are
2/17/2008
Page 8 of 8
familiar with object-oriented programming (OOP) will realise that the namespace and ensemble mechanisms
provide many of the same encapsulation advantages. Indeed, many OO extensions for Tcl build on top of the
powerful namespace mechanism.
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
One difference between Tcl and most other compilers is that Tcl will allow an executing program to create new
commands and execute them while running.
A tcl command is defined as a list of strings in which the first string is a command or proc. Any string or list which
meets this criteria can be evaluated and executed.
The eval command will evaluate a list of strings as though they were commands typed at the % prompt or sourced
from a file. The eval command normally returns the final value of the commands being evaluated. If the commands
being evaluated throw an error (for example, if there is a syntax error in one of the strings), then eval will will
throw an error.
Note that either concat or list may be used to create the command string, but that these two commands will
create slightly different command strings.
eval arg1 ??arg2?? ... ??argn??
Evaluates arg1 - argn as one or more Tcl commands. The args are concatenated into a string, and passed to
tcl_Eval to evaluate and execute.
Eval returns the result (or error code) of that evaluation.
Example
set cmd {puts "Evaluating a puts"}
puts "CMD IS: $cmd"
eval $cmd
2/17/2008
Page 2 of 2
$cmd
}
puts "\nThe body of newProcB is: \n[info body newProcB]\n"
puts "newProcB returns: [newProcB]"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
There may be some unexpected results when you try to compose command strings for eval.
For instance
eval puts OK
As long as you keep track of how the arguments you present to eval will be grouped, you can use many methods
of creating the strings for eval, including the string commands and format.
The recommended methods of constructing commands for eval is to use the list and lappend commands. These
commands become difficult to use, however if you need to put braces in the command, as was done in the previous
file://E:\vss\More command construction - format, list.htm
2/17/2008
Page 2 of 3
lesson.
The example from the previous lesson is re-implemented in the example code using lappend.
The completeness of a command can be checked with info complete. Info complete can also be used in an
interactive program to determine if the line being typed in is a complete command, or the user just entered a
newline to format the command better.
info complete string
If string has no unmatched brackets, braces or parentheses, then a value of 1 is returned, else 0 is returned.
Example
set cmd "OK"
eval puts $cmd
set cmd "puts" ; lappend cmd {Also OK}; eval $cmd
set cmd "NOT OK"
eval puts $cmd
eval [format {%s "%s"} puts "Even This Works"]
set cmd "And even this can be made to work"
eval [format {%s "%s"} puts $cmd ]
set tmpFileNum 0;
set cmd {proc tempFileName }
lappend cmd ""
lappend cmd "global num; incr num; return \"/tmp/TMP.[pid].\$num\""
eval $cmd
puts "\nThis is the body of the proc definition:"
puts "[info body tempFileName]\n"
2/17/2008
Page 3 of 3
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
The Tcl interpreter does only one substitution pass during command evaluation. Some situations, such as placing
the name of a variable in a variable, require two passes through the substitution phase. In this case, the subst
command is useful.
Subst performs a substitution pass without performing any execution of commands except those required for the
substitution to occur, ie: commands within [] will be executed, and the results placed in the return string.
In the example code:
puts "[subst $$c]\n"
shows an example of placing a variable name in a variable, and evaluating through the indirection.
The format command can also be used to force some levels of substitution to occur.
subst ?-nobackslashes? ?-nocommands? ?-novariables? string
Passes string through the Tcl substitution phase, and returns the original string with the backslash sequences,
commands and variables replaced by their equivalents.
If any of the -no... arguments are present, then that set of substitutions will not be done.
NOTE: subst does not honor braces or quotes.
Example
2/17/2008
Page 2 of 2
set a "alpha"
set b a
puts
puts
puts
puts
{a
"a
"a
"a
and
and
and
and
b
b
b
b
with
with
with
with
no substitution: $a $$b}
one pass of substitution: $a $$b"
subst in braces: [subst {$a $$b}]"
subst in quotes: [subst "$a $$b"]\n"
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
Tcl also supports commands to change and display the current working directory.
These are:
cd ?dirName?
Changes the current directory to dirName (if dirName is given, or to the $HOME directory if dirName is not
given. If dirName is a tilde (~, cd changes the working directory to the users home directory. If dirName starts
with a tilde, then the rest of the characters are treated as a login id, and cd changes the working directory to
that user's $HOME.
pwd
Returns the current directory.
Example
set dirs [list TEMPDIR]
puts "[format "%-15s
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 5
Index
Next lesson
In previous lessons we discussed how the return command could be used to return a value from a proc. In Tcl, a
proc may return a value, but it always returns a status.
When a Tcl command or procedure encounters an error during its execution, the global variable errorInfo is set,
and an error condition is generated. If you have proc a that called proc b that called c that called d , if d generates
an error, the "call stack" will unwind. Since d generates an error, c will not complete execution cleanly, and will
have to pass the error up to b , and in turn on to a. Each procedure adds some information about the problem to
the report. For instance:
proc a {} {
b
}
proc b {} {
c
}
proc c {} {
d
}
proc d {} {
some_command
}
a
2/17/2008
Page 2 of 5
"some_command"
(procedure "d" line 2)
invoked from within
"d"
(procedure "c" line 2)
invoked from within
"c"
(procedure "b" line 2)
invoked from within
"b"
(procedure "a" line 2)
invoked from within
"a"
(file "errors.tcl" line 16)
This actually occurs when any exception condition occurs, including break and continue. The break and continue
commands normally occur within a loop of some sort, and the loop command catches the exception and processes it
properly, meaning that it either stops executing the loop, or continues on to the next instance of the loop without
executing the rest of the loop body.
It is possible to "catch" errors and exceptions with the catch command, which runs some code, and catches any
errors that code happens to generate. The programmer can then decide what to do about those errors and act
accordingly, instead of having the whole application come to a halt.
For example, if an open call returns an error, the user could be prompted to provide another file name.
A Tcl proc can also generate an error status condition. This can be done by specifying an error return with an option
to the return command, or by using the error command. In either case, a message will be placed in errorInfo, and
the proc will generate an error.
error message ?info? ?code?
Generates an error condition and forces the Tcl call stack to unwind, with error information being added at
each step.
2/17/2008
Page 3 of 5
If info or code are provided, the errorInfo and errorCode variables are initialized with these values.
catch script ?varName?
Evaluates and executes script. The return value of catch is the status return of the Tcl interpreter after it
executes script If there are no errors in script, this value is 0. Otherwise it is 1.
If varName is supplied, the value returned by script is placed in varName if the script successfully executes. If not,
the error is placed in varName.
return ?-code code? ?-errorinfo info? ?-errorcode errorcode? ?value?
The next value specifies the return status. code must be one of:
l ok - Normal status return
l error - Proc returns error status
l return - Normal return
l break - Proc returns break status
l continue - Proc returns continue status
These allow you to write procedures that behave like the built in commands break, error, and continue.
-errorinfo info
info will be the
-errorcode errorcode
is a global variable that contains the error information from commands that have failed.
errorCode
is a global variable that contains the error code from command that failed. This is meant to be in a
format that is easy to parse with a script, so that Tcl scripts can examine the contents of this variable, and
decide what to do accordingly.
errorCode
2/17/2008
Page 4 of 5
Example
proc errorproc {x} {
if {$x > 0} {
error "Error generated by error" "Info String for error" $x
}
}
catch errorproc
puts "after bad proc call: ErrorCode: $errorCode"
puts "ERRORINFO:\n$errorInfo\n"
set errorInfo "";
catch {errorproc 0}
puts "after proc call with no error: ErrorCode: $errorCode"
puts "ERRORINFO:\n$errorInfo\n"
catch {errorproc 2}
puts "after error generated in proc: ErrorCode: $errorCode"
puts "ERRORINFO:\n$errorInfo\n"
proc returnErr { x } {
return -code error -errorinfo "Return Generates This" -errorcode "-999"
}
catch {returnErr 2}
puts "after proc that uses return to generate an error: ErrorCode: $errorCode"
puts "ERRORINFO:\n$errorInfo\n"
proc withError {x} {
set x $a
}
catch {withError 2}
puts "after proc with an error: ErrorCode: $errorCode"
puts "ERRORINFO:\n$errorInfo\n"
catch {open [file join no_such_directory no_such_file] r}
2/17/2008
Page 5 of 5
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
When you are debugging Tcl code, sometimes it's useful to be able to trace either the execution of the code, or
simply inspect the state of a variable when various things happen to it. The trace command provides these
facilities. It is a very powerful command that can be used in many interesting ways. It also risks being abused, and
can lead to very difficult to understand code if it is used improperly (for instance, variables seemingly changing
magically), so use it with care.
There are three principle operations that may be performed with the trace command:
l
l
l
add,
which has the general form: trace add type ops ?args?
info, which has the general form: trace info type name
remove, which has the general form: trace remove type name opList command
Which are for adding traces, retrieving information about traces, and removing traces, respectively. Traces can be
added to three kinds of "things":
l
l
l
- Traces added to variables are called when some event occurs to the variable, such as being written
to or read.
command - Traces added to commands are executed whenever the named command is renamed or deleted.
execution - Traces on "execution" are called whenever the named command is run.
variable
Traces on variables are invoked on four separate conditions - when a variable is accessed or modified via the array
command, when the variable is read or written, or when it's unset. For instance, to set a trace on a variable so that
when it's written to, the value doesn't change, you could do this:
proc vartrace {oldval varname element op} {
upvar $varname localvar
2/17/2008
Page 2 of 3
In the above example, we create a proc that takes four arguments. We supply the first, the old value of the
variable, because write traces are triggered after the variable's value has already been changed, so we need to
preserve the original value ourselves. The other three arguments are the variable's name, the element name if the
variable is an array (which it isn't in our example), and the operation to trace - in this case, write. When the trace is
called, we simply set the variable's value back to its old value. We could also do something like generate an error,
thus warning people that this variable shouldn't be written to. Infact, this would probably be better. If someone else
is attempting to understand your program, they could become quite confused when they find that a simple set
command no longer functions!
The command and execution traces are intended for expert users - perhaps those writing debuggers for Tcl in Tcl
itself - and are therefore not covered in this tutorial, see the trace man page for further information.
Example
proc traceproc {variableName arrayElement operation} {
set op(write) Write
set op(unset) Unset
set op(read) Read
set level [info level]
incr level -1
if {$level > 0} {
set procid [info level $level]
} else {
set procid "main"
2/17/2008
Page 3 of 3
}
if {![string match $arrayElement ""]} {
puts "TRACE: $op($operation) $variableName($arrayElement) in $procid"
} else {
puts "TRACE: $op($operation) $variableName in $procid"
}
}
proc testProc {input1 input2} {
upvar $input1 i
upvar $input2 j
set i 2
set k $j
}
trace add variable i1 write traceproc
trace add variable i2 read traceproc
trace add variable i2 write traceproc
set i2 "testvalue"
puts "\ncall testProc"
testProc i1 i2
puts "\nTraces on i1: [trace info variable i1]"
puts "Traces on i2: [trace info variable i2]\n"
trace remove variable i2 read traceproc
puts "Traces on i2 after vdelete: [trace info variable i2]"
puts "\ncall testProc again"
testProc i1 i2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 2
Index
Next lesson
Scripts are much more useful if they can be called with different values in the command line.
For instance, a script that extracts a particular value from a file could be written so that it prompts for a file name,
reads the file name, and then extracts the data. Or, it could be written to loop through as many files as are in the
command line, and extract the data from each file, and print the file name and data.
The second method of writing the program can easily be used from other scripts. This makes it more useful.
The number of command line arguments to a Tcl script is passed as the global variable argc . The name of a Tcl
script is passed to the script as the global variable argv0 , and the rest of the command line arguments are passed
as a list in argv. The name of the executable that runs the script, such as tclsh is given by the command info
nameofexecutable
Another method of passing information to a script is with environment variables. For instance, suppose you are
writing a program in which a user provides some sort of comment to go into a record. It would be friendly to allow
the user to edit their comments in their favorite editor. If the user has defined an EDITOR environment variable, then
you can invoke that editor for them to use.
Environment variables are available to Tcl scripts in a global associative array env . The index into env is the name
of the environment variable. The command puts "$env(PATH)" would print the contents of the PATH environment
variable.
Example
puts "There are $argc arguments to this script"
2/17/2008
Page 2 of 2
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
Tcl I/O is based on a the concept of channels. A channel is conceptually similar to a FILE * in C, or a stream in shell
programming. The difference is that a channel may be a either a stream device like a file, or a connection oriented
construct like a socket.
A stream based channel is created with the open command, as discussed in lesson 26. A socket based channel is
created with a socket command. A socket can be opened as either as a client, or as a server.
If a socket channel is opened as a server, then the tcl program will 'listen' on that channel for another task to
attempt to connect with it. When this happens, a new channel is created for that link (server-> new client), and the
tcl program continues to listen for connections on the original port number. In this way, a single Tcl server could be
talking to several clients simultaneously.
When a channel exists, a handler can be defined that will be invoked when the channel is available for reading or
writing. This handler is defined with the fileevent command. When a tcl procedure does a gets or puts to a blocking
device, and the device isn't ready for I/O, the program will block until the device is ready. This may be a long while
if the other end of the I/O channel has gone off line. Using the fileevent command, the program only accesses an
I/O channel when it is ready to move data.
Finally, there is a command to wait until an event happens. The vwait command will wait until a variable is set. This
can be used to create a semaphore style functionality for the interaction between client and server, and let a
controlling procedure know that an event has occurred.
Look at the example, and you'll see the socket command being used as both client and server, and the fileevent
and vwait commands being used to control the I/O between the client and server.
Note in particular the flush commands being used. Just as a channel that is opened as a pipe to a command doesn't
2/17/2008
Page 2 of 3
send data until either a flush is invoked, or a buffer is filled, the socket based channels don't automatically send
data.
socket -server command ?options? port
The socket command with the -server
flag starts a server socket listing on port port. When a connection occurs
on port, the proc command is called with the arguments:
l channel - The channel for the new client
l address - The IP Address of this client
l port The port that is assigned to this client
without the -server option opens a client connection to the system with IP Address host
and port address port. The IP Address may be given as a numeric string, or as a fully qualified domain
address.
To connect to the local host, use the address 127.0.0.1 (the loopback address).
The fileevent command defines a handler to be invoked when a condition occurs. The conditions are readable,
which invokes script when data is ready to be read on channelID, and writeable, when channelID is ready to
receive data. Note that end-of-file must be checked for by the script.
vwait varName
The vwait
command pauses the execution of a script until some background action sets the value of varName. A
background action can be a proc invoked by a fileevent, or a socket connection, or an event from a tk widget.
Examples
proc serverOpen {channel addr port} {
global connected
set connected 1
fileevent $channel readable "readLine Server $channel"
puts "OPENED"
}
2/17/2008
Page 3 of 3
Previous lesson
Index
Next lesson
2/17/2008
Page 1 of 3
Index
Next lesson
The clock command provides access to the time and date functions in Tcl. Depending on the subcommands
invoked, it can acquire the current time, or convert between different representations of time and date.
The clock command is a platform independent method of getting the display functionality of the unix date
command, and provides access to the values returned by a unix gettime() call.
clock seconds
The clock seconds command returns the time in seconds since the epoch. The date of the epoch varies for
different operating systems, thus this value is useful for comparison purposes, or as an input to the clock
format command.
clock format clockValue ?-gmt boolean? ?-format string?
The format subcommand formats a clockvalue (as returned by clock clicks into a human readable string.
The -gmt switch takes a boolean as the second argument. If the boolean is 1 or True, then the time will be
formatted as Greenwich Mean Time, otherwise, it will be formatted as local time.
The -format option controls what format the return will be in. The contents of the string argument to format
has similar contents as the format statement (as discussed in lesson 19, 33 and 34). In addition, there are
several more %* descriptors that can be used to describe the output.
These include:
l %a . . . . Abbreviated weekday name (Mon, Tue, etc.)
l %A . . . . Full weekday name (Monday, Tuesday, etc.)
l %b . . . . Abbreviated month name (Jan, Feb, etc.)
l %B . . . . Full month name (January, February, etc.)
2/17/2008
l
l
l
l
l
Page 2 of 3
%D . . . . Date as %m/%d/%y
%r. . . . . Time as %I:%M:%S %p
l %R . . . . Time as %I:%M
l %T . . . . Time as %I:%M:%S
l %Z . . . . Time Zone Name
clock scan dateString
The scan subcommand converts a human readable string to a system clock value, as would be returned by
clock seconds
l
l
2/17/2008
Page 3 of 3
monthname dd
dd monthname yy
dd monthname
day, dd monthname yy
Example
set systemTime [clock seconds]
puts
puts
puts
puts
set
set
set
set
puts "The book and movie versions of '2001, A Space Oddysey' had a"
puts "discrepancy of [expr {$bookSeconds - $movieSeconds}] seconds in how"
puts "soon we would have sentient computers like the HAL 9000"
Previous lesson
Index
Next lesson
2/17/2008
Timing scripts
Page 1 of 2
Timing scripts
Previous lesson
Index
Next lesson
The simplest method of making a script run faster is to buy a faster processor. Unfortunately, this isn't always an
option. You may need to optimize your script to run faster. This is difficult if you can't measure the time it takes to
run the portion of the script that you are trying to optimize.
The time command is the solution to this problem. time will measure the length of time that it takes to execute a
script. You can then modify the script, rerun time and see how much you improved it.
After you've run the example, play with the size of the loop counters in timetst1 and timetst2. If you make the inner
loop counter 5 or less, it may take longer to execute timetst2 than it takes for timetst1. This is because it takes time
to calculate and assign the variable k, and if the inner loop is too small, then the gain in not doing the multiply
inside the loop is lost in the time it takes to do the outside the loop calculation.
time script ?count?
Returns the number of milliseconds it took to execute script. If count is specified, it will run the script count
times, and average the result. The time is elapsed time, not CPU time.
Example
proc timetst1 {lst} {
set x [lsearch $lst "5000"]
return $x
}
proc timetst2 {array} {
upvar $array a
file://E:\vss\Timing scripts.htm
2/17/2008
Timing scripts
Page 2 of 2
return $a(5000);
}
# Make a long list and a large array.
for {set i 0} {$i < 5001} {incr i} {
set array($i) $i
lappend list $i
}
puts "Time for list search: [ time {timetst1 $list} 10]"
puts "Time for array index: [ time {timetst2 array} 10]"
Previous lesson
file://E:\vss\Timing scripts.htm
Index
Next lesson
2/17/2008
Page 1 of 5
Index
Next lesson
The previous lessons have shown how to use channels with files and blocking sockets. Tcl also supports nonblocking reads and writes, and allows you to configure the sizes of the I/O buffers, and how lines are terminated.
A non-blocking read or write means that instead of a gets call waiting until data is available, it will return
immediately. If there was data available, it will be read, and if no data is available, the gets call will return a 0
length.
If you have several channels that must be checked for input, you can use the fileevent command to trigger reads
on the channels, and then use the fblocked command to determine when all the data is read.
The fblocked and fconfigure commands provide more control over the behavior of a channel.
The fblocked command checks whether a channel has returned all available input. It is useful when you are
working with a channel that has been set to non-blocking mode and you need to determine if there should be data
available, or if the channel has been closed from the other end.
The fconfigure command has many options that allow you to query or fine tune the behavior of a channel
including whether the channel is blocking or non-blocking, the buffer size, the end of line character, etc.
fconfigure channel ?param1? ?value1? ?param2? ?value2?
Configures the behavior of a channel. If no param values are provided, a list of the valid configuration
parameters and their values is returned.
If a single parameter is given on the command line, the value of that parameter is returned.
If one or more pairs of param/value pairs are provided, those parameters are set to the requested value.
2/17/2008
Page 2 of 5
is done, the fileevent triggers the read, but the gets can't read characters because there is no newline. The gets
returns a -1, and fblocked returns a 1. When a bare newline is sent, the data in the input buffer will become
available, and the gets returns 18, and fblocked returns 0.
2/17/2008
Page 3 of 5
Example
proc serverOpen {channel addr port} {
puts "channel: $channel - from Address: $addr Port: $port"
puts "The default state for blocking is: [fconfigure $channel -blocking]"
puts "The default buffer size is: [fconfigure $channel -buffersize ]"
# Set this channel to be non-blocking.
fconfigure $channel -blocking 0
set bl [fconfigure $channel -blocking]
puts "After fconfigure the state for blocking is: $bl"
# Change the buffer size to be smaller
fconfigure $channel -buffersize 12
puts "After Fconfigure buffer size is: [fconfigure $channel -buffersize ]\n"
# When input is available, read it.
fileevent $channel readable "readLine Server $channel"
}
proc readLine {who channel} {
global didRead
global blocked
puts "There is input for $who on $channel"
set len [gets $channel line]
set blocked [fblocked $channel]
puts "Characters Read: $len Fblocked: $blocked"
if {$len < 0} {
if {$blocked} {
puts "Input is blocked"
} else {
puts "The socket was closed - closing my end"
close $channel;
}
} else {
puts "Read $len characters: $line"
2/17/2008
Page 4 of 5
2/17/2008
Page 5 of 5
Previous lesson
Index
Next lesson
2/17/2008
Child interpreters
Page 1 of 3
Child interpreters
Previous lesson
Index
For most applications, a single interpreter and subroutines are quite sufficient. However, if you are building a clientserver system (for example) you may need to have several interpreters talking to different clients, and maintaining
their state. You can do this with state variables, naming conventions, or swapping state to and from disk, but that
gets messy.
The interp command creates new child interpreters within an existing interpreter. The child interpreters can have
their own sets of variables, commands and open files, or they can be given access to items in the parent
interpreter.
If the child is created with the -safe option, it will not be able to access the file system, or otherwise damage your
system. This feature allows a script to evaluate code from an unknown (and untrusted) source.
The names of child interpreters are a hierarchical list. If interpreter foo is a child of interpreter bar, then it can be
accessed from the toplevel interpreter as {bar foo}.
The primary interpreter (what you get when you type tclsh) is the empty list {}.
The interp command has several subcommands and options. A critical subset is:
interp create -safe name
Creates a new interpreter and returns the name. If the -safe option is used, the new interpreter will be unable
to access certain dangerous system facilities.
interp delete name
This is similar to the regular eval command, except that it evaluates the script in the child interpreter instead
file://E:\vss\Child interpreters.htm
2/17/2008
Child interpreters
Page 2 of 3
of the primary interpreter. The interp eval command concatenates the args into a string, and ships that line to
the child interpreter to evaluate.
interp alias srcPath srcCmd targetPath targetCmd arg arg
The interp alias command allows a script to share procedures between child interpreters or between a child
and the primary interpreter.
Note that slave interpreters have a separate state and namespace, but do not have separate event loops. These are
not threads, and they will not execute independently. If one slave interpreter gets stopped by a blocking I/O
request, for instance, no other interpreters will be processed until it has unblocked.
The example below shows two child interpreters being created under the primary interpreter {}. Each of these
interpreters is given a variable name which contains the name of the interpreter.
Note that the alias command causes the procedure to be evaluated in the interpreter in which the procedure was
defined, not the interpreter in which it was evaluated. If you need a procedure to exist within an interpreter, you
must interp eval a proc command within that interpreter. If you want an interpreter to be able to call back to the
primary interpreter (or other interpreter) you can use the interp alias command.
Example
set i1 [interp create firstChild]
set i2 [interp create secondChild]
puts "first child interp: $i1"
puts "second child interp: $i2\n"
# Set a variable "name" in each child interp, and
# create a procedure within each interp
# to return that value
foreach int [list $i1 $i2] {
interp eval $int [list set name $int]
interp eval $int {proc nameis {} {global name; return "nameis: $name";} }
}
file://E:\vss\Child interpreters.htm
2/17/2008
Child interpreters
Page 3 of 3
Previous lesson
file://E:\vss\Child interpreters.htm
Index
2/17/2008