Tcl Tutorial



Introduction

This is a very short introduction to the Tcl script language. If you just can't wait, I hope this will make you able to read and to understand simple Tcl code.

In many points, Tcl is similar to C, especially for loop structures, function definitions and mathematical or conditional expressions. In other points, such as expression evaluation and list data structures, you will notice that Tcl has inherited from the benefits of the Scheme language.

In Tcl, all data is represented as strings.

Commands evaluation

Each Tcl command call is a sentence of the form : command arg1 arg2 arg3 ...

The Tcl evaluator take each word of this sentence and evaluate it. After evaluation of each word, the first word (command) is considered to be a function name and this function is executed with as arguments the following words.

To evaluate a word, the interpretor has to do the following substitutions in the word string :

  • If the word is surrounded by " ", this word may contain spaces, but substitution is still applicable inside the quotations. Inside the quotation, there may be spaces and carriage returns.
  • If a word is surrounded by { }, this word is unaffected (substitution is thus not applicable on this word). Inside the braces, there may be spaces and carriage returns. Moreover, the { } braces may be nested.
  • If a part of the word is surrounded by [ ], this part is considered as a command sentence : the text within the brackets is evaluated as a Tcl command and replaced with the result.
  • where substitution is applicable, every string beginning with $ is replaced with the variable represented by this string. This string is ended by a space, a '-' or a ','.

Examples

  1. set a "World !"
    In the evaluation of the 3 words 'set', 'a' and '"World !"', no substitution has to be done, only the " " are removed. The command 'set' is then executed with as parameters 'a' and 'World !'. This command tell Tcl to define a new variable 'a' (if not already defined) and to set its value to 'World !'.
  2. set b "Hello $a"
    Set the variable 'b' to 'Hello World !'. Here, the variable substitution has occurred inside the second parameter where the variable 'a' is replaced by its value.
  3. set c [string range $b 0 3]
    Set the variable c to 'Hell', which is the 4 first letters of 'Hello World !'. In this case, The part between [ ] has been executed as a command

If you want to break a command sentence in lines you can only do it inside the { } brace or in the " " quotation or you can break the line with a '\' at the end of any break line.

Example


if {$c == "Hell"} {
   puts "Oh god !"
} else {
   puts "Peace !"
}

This test the value of the variable c. If it is the string 'Hell' it prints 'Oh god !' on screen, otherwise, it prints 'Peace !'. In this sentence, Tcl see 5 words :

  • 'if' is the first : nothing to be evaluated.
  • '$c == "Hell"' is the second : because of the surrounding curly braces, there is no further evaluation on this word.
  • 'puts "Oh god !"' : for the same reason, no further evaluation
  • 'else' : nothing to do.
  • 'puts "Peace !"' : no further evaluation.

The first word, 'if' is seen as the command and this command is executed with as parameters the 4 following words. That is later that the condition '$c == "Hell"' is evaluated, during the execution of the if command.

Notice where we placed the line breaks (inside the { }).

Strings and Lists

Under Tcl, the value of each variable is stored as a string. Even if you want to save a number in a variable, this number is transformed into a string.

As a special type of string, the list deserve a special attention in data representation in Tcl. The list is nothing more than a string with, as elements separator, the space. A list may contains sublists.

Example


% set list {12 {78 5} 45 "Im a not a number"}
12 {78 5} 45 "Im a not a number"
% set sublist1 [lindex $list 1]
78 5
% set sublist2 [lindex $list 3]
Im a not a number
% lindex $sublist2 2
not

Mathematics expression

Whereas all variables are of type string, the mathematical operations internally uses float and integer number representation to produce their results. The command that calculate mathematical expression is 'expr'.

Example


% set result [expr (4+6)/4]
2
% set result [expr (4.0+6)/4]
2.5

In the first calculation, the interpretor has used the integer number representation. In the second, it has used the float number representation.

How to display something ?

To display a string, you can use the command 'puts'

Example


% set variable 255
% puts "The number $variable"
The number 255
% puts [format "The number %d is equal to 0x%02X" \
  $variable $variable]
The number 255 is equal to 0xFF

As it can be seen in the previous example, the command format is very similar to the C command 'printf'.

Control flow

The following commands are similar to the C equivalent. Only 'foreach' has no C equivalent (have a look at the example to see what it do).

  • if {...condition...} {...body...}
  • while {...condition...} {body}
  • for {... init ...} {...condition...} {...increment...} {...body...}
  • foreach varnames {...list...} {...body...}
the '...condition...' is evaluated in the same way that it should be with command 'expr'.

Examples

  1. while
    
    % while {$i<4} {
    > puts "$i*$i is [expr $i*$i]"
    > incr i
    > }
    0*0 is 0
    1*1 is 1
    2*2 is 4
    3*3 is 9
    
  2. for
    
    % for {set i 0} {$i<4} {incr i} {
    > puts "$i*$i is [expr $i*$i]"
    > }
    0*0 is 0
    1*1 is 1
    2*2 is 4
    3*3 is 9
    
  3. foreach
    
    % set observations \
      {Bruxelles 15 22 London 12 19 Paris 18 27}
    Bruxelles 15 22 London 12 19 Paris 18 27
    % foreach {town Tmin Tmax} $observations {
    > set Tavg [expr ($Tmin+$Tmax)/2.0]
    > puts "$town $Tavg"
    > }
    Bruxelles 18.5
    London 15.5
    Paris 22.5
    

Array

Arrays are always unidimensional but the index is a string. If you use a separator in the index string (such as ',', '-'), you can get the same effect than with a multidimensional array in other languages.

Example


% set observations \
  {Bruxelles 15 22 London 12 19 Paris 18 27}
Bruxelles 15 22 London 12 19 Paris 18 27
% foreach {town Tmin Tmax} $observations {
set obs($town-min) $Tmin
set obs($town-max) $Tmax
}
% parray obs
obs(Bruxelles-max) = 22
obs(Bruxelles-min) = 15
obs(London-max)    = 19
obs(London-min)    = 12
obs(Paris-max)     = 27
obs(Paris-min)     = 18

Procedures

Procedures are the equivalent of the C functions.

Example


% proc sum2 {a b} {
>  return [expr $a+$b]
> }

if a procedure does not contain any 'return' statement, the default return value is the return value of the last evaluated function in this procedure. So the following script is perfectly equivalent :


% proc sum2 {a b} {
>   expr $a + $b
> }

To call the 'sum2' function, we do the following :


% sum2 12 5
17

The special argument name 'args' contains a list with the rest of the arguments

Example


% proc sum {args} {
>   set result 0
>   foreach n $args {
>      set result [expr $result+$n]
>   }
>   return $result
> }
% sum 12 9 6 4
31
it is also possible to specify default parameters. So, if you don't specify the last parameters, the default values will be substituted.

Example


% proc count {start end {step 1}} {
>   for {set i $start} {$i<=$end} {incr i $step} {
>     puts $i
>   }
> }
% count 1 3
1
2
3
% count 1 5 2
1
3
5

If you want to use global variables in a function, you have to declare it as global.

Example


% set global_counter 3
% proc incr_counter {} {
>    global global_counter
>    incr global_counter
> }
% incr_counter
4
% set global_counter
4

You can also declare a table as global.

Example


% set counter(value) 3
% set counter(active) 1
% proc incr_counter {} {
>    global counter
>    if {$counter(active)} {
>       incr counter(value)
>    }
> }
% incr_counter
4
% set counter(active) 0
0
% incr_counter
4

Eval

The 'eval' command

  • concatenate all its arguments in one string
  • splits this string using spaces as separators
  • evaluate the command sentence formed by all the substrings

In the following example, we used the function 'sum' that we have already defined.

Example


%  proc average {args} {
>     return [expr [eval sum $args] / [llength $args]] 
>  } 
% average 45.0 65.0 78.0 55.0
60.75

If you had omitted the 'eval' command in the previous example, the 'sum' procedure would have returned an error because 'sum' should be called with only one string argument (in the previous example, this argument would have been '45.0 65.0 78.0 55.0') while 'sum' is expecting numerical arguments.

uplevel, upvar

With the 'upvar' command, you can access a variable which belongs to a higher level of the procedure call stack.

Example


% proc decr {n steps} {
>   upvar $n upa
>   set upa [expr $upa - $steps]
> }
% set nb 12
12
% decr nb 3
9
% puts $nb
9

In the previous example, the parameter 'n' gets the value 'nb' (the string 'nb' !) if we type 'decr nb 3'. The command 'upvar $n upa' means that the variable 'upa' becomes a synonym to the variable 'nb' (coming from a higher level of the stack).

With the 'uplevel' command, you can evaluate something on higher level in the stack.

Example


% proc do {todo condition} {
>   set ok 1
>   while {$ok} {
>     uplevel $todo
>     if {[uplevel "expr $condition"]==0} {set ok 0}
>   }
> }
% set i 0
0
% do {
puts $i
incr i
} {$i<4}
0
1
2
3

Inside the procedure 'do', the evaluation of the script 'todo' and the conditional 'condition' has to made on a higher level of stack (in the same way that if they were evaluated from out of 'do').

error and catch

If you insert an 'error' command in your code, this command will stop the execution of your script and return the error message that follow the 'error' command. With the command 'catch', you can also intercept a error to avoid that your script stops on an error.

If 'catch' return 0, it means that no error occurred while evaluating the script send as parameter of catch. If 'catch' return 1, it means that an error occurred.

Example


% proc div {a b} {
>   if {$b==0} {
>      error "divided by zero"
>   } else {
>      return [expr $a/$b]
>   }
> }
% div 8 3
2
% div 8 0
divide by zero
% catch {div 8 3}
0
% catch {div 8 0}
1
% catch {set result [div 8 3]}
0
% catch {set result [div 8 0]}
1

The last call is completely equivalent to


catch {div 8 3} result