Tcl syntax
Tcl scripts are nothing more than plaintext files saved with 16-bit Unicode encoding. Such a script consists of one or more commands that are executed by the interpreter at runtime.
Each line of the script (with exceptions described as follows) consists of the command and zero or more command arguments, all separated by whitespaces (spaces or tabs).
If the line starts with the #
character, it is considered to be a comment and not processed by the interpreter.
If the line ends with the \
(backslash) character, the next line is considered to be a continuation of current one. This is useful for breaking long lines and improving the readability of the code.
You can also join separate commands using;, as shown in the following example:
puts "one"; puts "two"
Tcl commands documentation syntax
The comprehensive documentation for Tcl 8.5 is located at http://www.tcl.tk/doc/. This section does not intend to replace or compete with it, but to briefly introduce Tcl's grammar and rules. Therefore, we strongly recommend reading the documentation, especially the descriptions of the main commands.
Running the script
Let's revisit the main.tcl
script which was introduced earlier in this chapter:
#my first Tcl script puts "Hello World one more time!"
The first line is the comment. The second line has the puts
command with one parameter —the text string—in quotations marks. As we mentioned previously, you can run the script with:
tclsh main.tcl
However, in some cases, it would be far more comfortable to run it as a standalone program. On Windows, you can associate the .tcl
extension with the Tcl interpreter to achieve this. On Unix, the script will look like:
#!/usr/bin/tclsh #my first Tcl script puts "Hello World one more time!"
The first line starts with the #!
sequence, often referred to as shebang. The presence of these two characters at the very beginning of the executable file informs the system's program loader that the file is a script and also specifies the interpreter which should be used to run it. In this case, the first line issues an instruction to Unix shell, specifying that the script should be interpreted by /usr/bin/tclsh
. As the #
character itself is often a comment marker, the line will be ignored by the interpreter. However, it is not a good idea to hardcode the path, because it may be different for other systems, thus making our code not non-portable. A better solution is:
#!/bin/sh #my first Tcl script \ exec tclsh "$0" $@" puts "Hello World one more time!"
In this case, the script is interpreted by standard sh
shell. This shell is unable to run Tcl commands, but it will run the exec
command perfectly, effectively launching tclsh
, passing the script name and other arguments (if any). The trick is that we do not want the Tcl interpreter to process the exec
command, as it was intended for sh
only. In order to avoid this problem, the previous line ends with \
, so both the second and third lines of that script are treated like comments by the interpreter.
Recently, also another solution has gained popularity, that is:
#!/usr/bin/env tclsh #my first Tcl script puts "Hello World one more time!"
The env
command searches for tclsh
using system PATH variable and executes it, therefore, no direct path to it is required.
Running commands directly in the tclsh interpreter
In the case of "one-liner" examples, we can also easily enter them directly into the Tcl shell—you will recognize them by the %
prompt character. It is worth mentioning that the shell offers hints about the syntax of any command. This is extremely useful if you don't quite remember the required arguments or their order:
% puts wrong # args: should be "puts ?-nonewline? ?channelId? string"
The syntax of such a hint is commonly used throughout the Tcl documentation. Question marks indicate optional arguments (in this example, nonewline
or channelId
are both optional).
Variables
As with any other language, Tcl allows us to define variables. Let's consider the following example:
set myVariable "someValue" puts $myVariable
The first command set
creates a variable named myVariable
and sets its value to somevalue
. The second command, that is puts
, prints the variable's value on the screen. myVariable
is the variable name and the $
operator is used to get its value. When the interpreter starts processing the second line, it will first substitute all the variable values, so the line will effectively be puts "someValue"
, and then it will execute it. This will result in the following output:
someValue
To delete a variable, use the unset
command:
% unset myVariable % puts $MyVariable can't read "MyVariable": no such variable
Grouping
In an earlier example, we used "" to group Hello world one more time! into one single argument. Without it, we would get a syntactical error, as each word would be treated as separate argument for the puts
command:
% puts Hello world one more time! wrong # args: should be "puts ?-nonewline? ?channelId? string"
It is also worth mentioning that whitespaces could be also escaped using the \
character, as shown in the following example:
% puts Hello\ world\ one\ more\ time! Hello world one more time!
If the argument consists of only one word, grouping is not necessary, but it's still possible.
The text inside quotation marks is processed before being passed to the command. What this it means is that all references to variables are replaced with the variable's values, for example:
% set var World; puts "Hello $var" Hello World
The substitution gets complicated if you don't want to have any whitespace between the variable's value and the text that follows. In this case, you can end the variable name with '\' or put it into {}
, as in the following example:
% set var World; puts "Hello $vars!" can't read "vars": no such variable % puts "Hello $var\s!" Hello Worlds! % puts "Hello ${var}s!" Hello Worlds!
The third option is the preferred one because of its readability.
Grouping is also possible with curly braces, that is, {}
. The difference is that text inside the quotation marks is evaluated, and the text inside {}
is not, consider the following:
% set var World; puts {Hello $var} Hello $var
In the second case, $var
was not substituted with its real value World
, because it was inside {}
.
One more unique feature of curly braces is that they allow multiple lines inside them. In the code:
% puts {Hello World!} Hello World!
The new line character that terminates the first line is not processed, and therefore, it also breaks the output into two lines. Of course, it is legal to also use \:
% puts {Hello\ World!} Hello World!
Nesting commands
The ability to execute commands wouldn't be very valuable if there wasn't some way to provide the result of one command to another. In Tcl, we can use []
to execute a command and capture its return value:
% set var [info patchlevel]; puts "Current patchlevel is: $var" Current patchlevel is: 8.5.6
In this example, the output of the info patchlevel
command is used as the value for the var
variable. It is also perfectly possible to use []
inside quotation marks, as its content will be processed by the interpreter:
% puts "Current patchlevel is: [info patchlevel]" Current patchlevel is: 8.5.6
Data types
Basically, every variable in Tcl can be considered as a string. The internal representation of such a variable depends on its execution state (see Chapter 2, Advanced Tcl features for more details). Being a dynamic language, Tcl converts the value of a variable to a suitable type at runtime. Let's consider the following example:
% set x "2"; expr $x + 3 5 % set x "text"; expr $x + 3 invalid bareword "text" in expression "text + 3";
We introduce the new Tcl command expr
that allows arithmetic operations. In this example, the string"2"
is converted to an integer by expr
and it returns correct value, but the string text
causes an error, because it cannot be converted.
To operate on strings, the string
command is extremely useful. The convention of this command is that it accepts a subcommand name as the first argument. For example, if we assume that str
is a Hello World! string, in order to get its length, you can use:
% string length $str 12
Also, in order to reverse it, use:
% string reverse $str !dlroW olleH
The String match
command can be used to verify if the given string matches the pattern. The pattern is basically another string, but apart from normal characters, some special sequences can be also used:
Sequence |
Interpretation |
---|---|
|
Matches any, exactly one character |
|
Matches 0 or more of any of the characters |
|
Matches exactly one character, only that specified inside |
|
Allows escaping of any of the special characters, such as |
The command returns 1
if the string is successfully matched and it returns 0
otherwise:
% set str aStringToMatch % string match *ToMatch $str 1 % string match ?StringToMatch $str 1 % string match {[a-c]StringToMatch} $str 1 % string match {[b-c]StringToMatch} $str 0
Note that in the last two cases, this pattern is put into curly braces to prevent the interpreter from considering the content of the []
braces as a command to be executed.
Describing every string subcommand is beyond the scope of this chapter, the details can be found in the manual at http://www.tcl.tk/man/tcl8.5/TclCmd/string.htm.
It is worth mentioning at this point that depending on the context, the variable's value may be treated as a text string, a number, or a logical value, just as we will see in the following examples.
More than pure strings, Tcl introduces the concept of two interesting data types—lists and arrays (hashmaps).
Lists
A Tcl list holds an ordered sequence of elements such as strings, other lists, and other variables. To create a list, you can use the list
command:
set myList [list element1 element2 element3]
In this example, we create a list using three elements, separated with whitespaces, and assign the list to the myList
variable.
What is interesting is that there is no straight demarcation between the list and the string, and therefore, the following code also creates a list:
set myList "element1 element2 element3"
Curly braces are also possible here. The conclusion is that every string may be considered as a list. The advantage of using the list
command is that it takes care of correct formatting, adding braces and backslashes when necessary. Consider either of the following two commands:
set myList "one two {another list here}" set myList {one two {another list here}}
They will create a list in which the third element will be another list consisting of elements specified in curly braces. The same effect can be achieved using the list
command:
set myList [list one two [list another list here]]
From the user's point of view, a list is both a string and a list, because there is no visible difference:
% puts $myList one two {another list here}
As you can see, the list
command added the {}
braces at the correct points.
So why even bother with lists? Lists allow you to organize and structure your data, and the benefits of using them are that Tcl offers a set of commands that operates on lists. Lists tend to be extremely useful in typical scripts, therefore, we will briefly review the possible operations on them.
To get the number of elements in your list, use llength
. This command takes one parameter, that is, the list. The output is the number of elements in the list.
% llength [list one two three] 3
The command lindex
retrieves an element from a list. In basic usage, it takes the list and the index of the element as arguments. Just as in other programming languages, indexes start from zero in Tcl.
% set myList [list one two three]; lindex $myList 0 one
We can also specify the index as the special keyword end
, which indicates the last element. For example:
% set myList [list one two three]; lindex $myList end three
We can also specify end-<n>
, which indicates the nth
element from the end. The value end-0
is the same as the end
value; the value end-1
specifies the last but one item and so on. For example:
% set myList [list one two three]; lindex $myList end-1 two
If the retrieved element is another list, lindex
also has the ability to retrieve its element by providing additional indexes:
% set myList [list one two [list another list here]]; lindex $myList 2 0 another
In this example, the third element (its index value is 2)
of myList
is, in turn, another embedded list, and we retrieve its first element—string another
—by specifying an additional index value to lindex — 0
.
The lrange
command returns a subset of the original list. It accepts the list as the first argument followed by the index of the first and last items that should be included in the new list. Both the first and last elements are included in the resulting list. The index can be in the form of an integer or in the end
or end-<n>
forms.
This command returns a new list consisting of elements that is the start to end indexes:
% set myList {1 2 3 4 5}; puts [lrange $myList 1 3] 2 3 4
In this case, 1
is the starting index and 2
the ending one, so the returned list consists of the second and third elements of the original list.
We can use the lappend
command to add one or more elements to an existing list. It accepts the variable name of the list, followed by any number of elements to be added to the list. The command inserts new elements at the end of the specified variable and does not return the new list—instead, it is automatically set as the new value of the specified variable.
For example:
% set myList {one two}; lappend myList 3 4; puts $myList one two 3 4
This command can also be used to create new lists if the variable passed as the first argument does not exist.
The linsert
command can be used to insert one or more elements into a list at the specified index. Unlike lappend
, it takes the value of the list as its first argument, followed by the index to insert elements at and any number of elements to insert. For example:
% set myList {1 5}; set myList [linsert $myList 1 2 3 4]; puts $myList 1 2 3 4 5
As for any other list-related command, the index can be in the form of an integer or in the end
or end-<n>
forms.
The lset
command allows you to replace the values of elements with new ones. It accepts a variable name, and one or more indexes, followed by the new value to set. The changed list is stored back in the variable specified as the first argument.
For example, we can specify a single index to modify the nth
element of a list:
% set myList {1 2 3}; lset myList 1 0; puts $myList 1 0 3
Similar to lindex
, this command can access and modify elements of sub-lists. For example:
% set myList [list one two [list another list here]]; lsert myList end 0 new; puts $mylist One two {new list here}
The lreplace
command is also similar to it, but allows replacing a set of elements. It accepts the list as the first argument, the start and end indexes to replace, and the new items to insert instead of the specified elements. The command removes elements between start and end inclusively and inserts new items in their place. Like linsert
, it does not alter an existing list, but returns a new one. For example:
% set myList {1 2 3 4 5}; puts [lreplace $myList 1 3 0 0] 1 0 0 5
In case you don't specify new replacement items, the command will only remove elements within a range:
% set myList {1 2 3 4 5}; puts [lreplace $myList 1 3] 1 5
The lsort
command can be used to sort lists in Tcl. For example:
% set myList {5 1 3 4 2}; puts [lsort $myList] 1 2 3 4 5
It is also possible to modify the sorting order by specifying additional options, for example -decreasing:
% set myList {5 1 3 4 2}; puts [lsort -decreasing $myList] 5 4 3 2 1
By default, the elements are compared as text values—therefore, when sorting 9, 10, 11, and, 2, the result would be 10, 11, 2, and 9. The following switches allow us to define how elements are compared, which affects the output of the sorting operation:
Switch |
Description |
Example |
---|---|---|
|
Compares elements as text; this is the default if no switch has been specified |
List: {a b c 9 10 11 2} Sorted: {2 9 10 11 a b c} |
|
Compares elements as integer values |
List: {9 10 11 2} Sorted: {2 9 10 11} |
|
Compares elements as floating point values |
List: {11.0 2.0 9 1.2 1} Sorted: {1 1.2 2.0 9 11.0} |
|
Compares elements using dictionary-style comparison; see the following paragraph for more details |
List: { Di beta al Beta} Sorted: {al Beta beta Di} |
Dictionary-based sorting is similar to text-based comparison, except that character case is ignored here. An exception is when both the elements are the same, in that case, the uppercase elements are put before lowercase elements. If strings contain numbers, they are compared as integers, not characters. For example, when sorting 9, 10, 11, and 2 using the -dictionary
switch, the result would be 2, 9, 10, and 11.
We can also tell lsort
to use all elements in the list as sub-lists and compare them only using specified elements of these sub-lists, by using the -index
option. The value for the option can be a single index or a list of indexes, where each element of the index list indicates an index in a sub-list. For example:
puts [lsort -integer -index 1 {{Alpha 2} {Beta 1} {Gamma 3}}] {Beta 1} {Alpha 2} {Gamma 3}
Another option is to use your own custom implementation of the comparison algorithm. To do so, you have to define a command that accepts two arguments (items from the list that is being sorted) and returns the integer value by complying to the following rules:
The value is negative when the first argument is considered to be lower than the second
The value is 0 when both arguments are considered equal
The value is a positive value when the first argument is higher than the second one
It is illustrated in the following example:
proc compareStrings {s1 s2} { return [expr {[string length $s1] - [string length $s2]}] } set animals [list snake crocodile monkey cat] puts "default: [lsort $animals]" puts "custom: [lsort -command compareStrings $animals]"
We have the animals
list that contains some animal names. The default implementation will sort it in the alphabetical order, but we will be able to define our own implementation in the form of the compareString
procedure, which will sort it in the order of the word's length:
default: cat crocodile monkey snake custom: cat snake monkey crocodile
An interesting property of the lsort
command is that when two elements are considered as the same, their order in the output is the same as the order in which they were taken as inputs. This can be used to sort lists of lists using multiple orders. We need to sort the list by each element we want it to be sorted using, but in the reverse order.
For example, we can sort a list where each element is a sub-list consisting of first name, last name, and age. If we want to order it by the last name and the decreasing order of age for the same last names, we need to sort it by age first and later by the last name:
set list {{John Doe 32} {Joe Smith 29} {Jane Doe 35}} set list [lsort -integer decreasing -index 2 $list] puts [lsort -dictionary -index 1 $temp]
The result of this sort would be:
{John Doe 32} {Jane Doe 35} {Joe Smith 29}
You can search through your list to find the element(s) that match your pattern using lsearch:
% set myList {zero one two three}; puts [lsearch $myList three] 3
This command looks for the element three
in myList
, and returns the index of the first occurrence of the searched element—3 (or -1
if element is not found). It will be able to accept options such as:
-glob
—specifies that a pattern is in the glob-style (similar to file mask, see thestring match
command's description for details: http://www.tcl.tk/man/tcl8.5/TclCmd/string.htm)-regexp
—similar to the previous option, but the pattern is compliant with the regular expression's syntax (http://www.tcl.tk/man/tcl8.5/TclCmd/re_syntax.htm)-inline
—the command will return the value of the element found instead of the index-all
—the command will return a list of all matching occurrences, instead of just the first one
These options can be combined together; for your convenience, here is an example of their usage:
set myList {zero one two three} puts "First match (glob) : \ [lsearch -glob $myList *t*] -> \ [lsearch -inline -glob $myList *t*]" puts "First match (regexp): \ [lsearch -regexp $myList e$] -> \ [lsearch -inline -regexp $myList e$]" puts "All matches (glob) : \ [lsearch -all -glob $myList *t*] -> \ [lsearch -all -inline -glob $myList *t*]" puts "All matches (regexp): \ [lsearch -all -regexp $myList e$] -> \ [lsearch -all -inline -regexp $myList e$]"
The expression *t*
will match any string containing the letter t
and the regular expression e$
will match any string that ends with the letter e
.
The output will be:
First match (glob) : 2 -> two First match (regexp): 1 -> one All matches (glob) : 2 3 -> two three All matches (regexp): 1 3 -> one three
The command lassign
allows you to assign values from the list to different variables on a single line. For example:
lassign {1 2 3} a b c
The effect of this command is equal to the execution of the following:
set a 1 set b 2 set c 3
The first argument is a list, and the consecutive arguments are names of the variables. If there are more variables than list elements, the remaining elements will be set to empty strings.
Arrays
Another important data type for storing a set of values is an array. In Tcl, arrays are essentially associative maps, because any string value is permitted as an array key (index). Consider the following example:
set myArray(key1) element1 set myArray(key2) element2 set myArray(3) element3
After looking at the first line, the interpreter knows that the myArray
variable will be an array, because of presence of the ()
braces. Inside these braces, the array key key1
is written, and it is mapped to the string element1
. We have three keys overall—key1
, key2
, and 3
(note that the last one is different from the first two, but still valid)—that point to three values: element1, element2
, and element3
respectively. The key name can contain any character, and also spaces (of course, with the correct syntax, for example, by using the Esc key with a backslash).
To manipulate array variables, use the array
command with the appropriate operator and arguments. The most common operations are:
The command array exists
returns the value 1
only when the variable passed as an argument exists and is an array:
% array exists myArray 1 % array exists fictionalArray 0
Otherwise, the command will return 0.
We can use the array unset
command to delete an array:
% array exists myArray 1 % array unset myArray; array exists myArray 0
We can also delete a specified key or keys by specifying the pattern to match the keys that we want to delete as follows:
array unset myArray prefix,*
This will delete all the elements of an array that starts with prefix
,.
You can convert an array into a list using array get:
% array get myArray index1 element1 index2 element2 3 element3
The list it returns consists of name-value pairs. This means that for each element in the array, this command returns its name as an element followed by its value as another element.
We can also use array get
to get specific elements of an array, by adding a pattern to match keys, similar to array unset
. For example:
array get myArray prefix,*
This will return information only for keys starting with prefix
,.
To reverse this operation and set one or more keys in an array from the name-value pairs, we can use array set
command:
% array set anotherArray "1 first 2 second" % array exists anotherArray 1
If you want to get a list of key names existing in a particular array, the array names
command comes handy:
% array names myArray index1 index2 3
Similarly, we can get only a subset of names by running:
array names myArray prefix,*
This will return only the keys which start with prefix
,.
array size
array size
returns the number of key-value mappings:
% array size myArray 3
Although arrays are popular and commonly used, there are some drawbacks to using them. They are not Tcl objects on their own, but rather a collection of variables, a way in which a particular variable name can store zero or more key-value relations:
set myarray(somekey) "my value"
If we set an array key with the previous command, myarray
is not a variable by itself, but rather each key and value pair is stored as a Tcl object. From the programming perspective, this has little impact. However, once you set an array, you cannot pass it as a variable; for example:
set myarray(somekey) "my value" return $myarray
The preceding code will raise an error (we will present return
later in this chapter). However, it is okay to reference it via the variable name in commands such as upvar, variable
, which are explained later throughout Chapter 2.
Also, only one dimensional arrays are supported—so the syntax like myArray(1)(2)
is incorrect. This can be bypassed by using some specific key name convention, for example with the '.' or ':' characters:
set my3DArray(0.0.0) element1 set my3DArray(0.0.1) element2 set my3DArray(1.0.1) element3
Effectively, my3DArray
can be treated as a three-dimensional array, with indexes separated by the character (or string) of your choice.
Dictionaries
To address the previously mentioned drawbacks of arrays, a new data type was introduced, which is quickly gaining acceptance and popularity among Tcl developers—dictionary. Dictionaries are somewhat similar to arrays in that they keep mapping between key and value, but a dictionary is treated as an object. Dictionaries can also be truly multi-dimensional. The other difference is that dictionaries are significantly faster than arrays.
The new command dict
was introduced to operate on dictionaries. The command is natively present in Tcl 8.5, but it can be also used in Tcl 8.4 by adding the dict
package.
To create a dictionary, use the dict create
command with the equal number of arguments; alternately, keys and values:
set myDict [dict create key1 value1 key2 value2 key3 value3]
The previous line creates a dictionary with three mappings and stores it in the myDict
variable.
This command also allows us to create multi-level dictionaries:
set a [dict create 1 [dict create 2 [dict create 3 value]]]
Here, in dictionary a
, the key 1
is mapped to other dictionary which has key 2
mapped to the third dictionary, wherein key 3
is mapped to value
.
To access the mapped value, you can use the dict get
command:
% dict get $myDict key2 value2 % dict get $a 1 2 3 value
The second command's invocation in this example demonstrates how to access a third-level variable.
These are only the basics, there are also many other operations available for dictionaries, these can be compared to the operations for arrays described earlier, because their concepts are similar. For the complete documentation of the dict
command, you can refer to http://www.tcl.tk/man/tcl8.5/TclCmd/dict.htm.
Mathematical expressions—expr
There is a separate command called expr
that handles all mathematical and logical operations. The command accepts an expression to evaluate. An interesting detail is that this expression can be passed as one argument, or as unlimited number of arguments. The following calls to expr
will return the same result:
expr "3 + 7 * 2" expr 3 + 7 * 2 expr {3 + 7 * 2} expr 3+7*2
All the preceding operations return the value 17 as the result.
In case of logical expressions, values of arguments, such as 1
(or any other numeric non-zero value), true, TRUE, yes
or YES
are considered as logical true, and 0, false, FALSE, no
, and NO
as logical false. The following is an example of a logical AND (the&&
operator) and OR (the operator ||
operator):
% expr yes && no 0 % expr yes || no 1
Flow control and loop instructions
Like almost any other language, Tcl has a set of commands—conditional and iterational—to control the flow of the program.
Flow control
The first one is the command if:
set condition 1 if {$condition} { puts "condition is true" } else { puts "condition is false" }
Depending on the boolean value of the condition
variable, the first or the second part of the command is executed. In this case, the output is:
condition is true
Note that the entire if
command call could be written in one line:
if {$condition} {puts "condition is true"} else {puts "condition is false"}
The first argument is an expression representing a logical condition. The value is evaluated in the same way as in case of the expr
command.
It is possible to skip else
and the second script body, or to add more conditions by using elseif
with another condition, and a script to execute in case the condition is true
.
The second command switch
allows us to decide what action should be taken based on the value passed; for example, consider the following example code:
set value second switch $value { first {puts "first value"} second {puts "second value"} default {puts "some other value"} }
As the value of value
variable is second
, the second script body is passed to the Tcl interpreter, resulting in the output second value on the screen.
It is possible to skip the default
section. Also, more than one acceptable value separated by the character may be defined for each of the sections:
switch $value { 1 1st - first {puts "first value"} 2 2nd - second {puts "second value"} default {puts "some other value"} }
The values 1, 1st
, or first
will all match with the first section.
Finally, it is possible to use patterns that we have used with string match
command to match the appropriate section, by specifying the -glob
option with the switch
command:
switch -glob $value { *st {puts "first value"} *nd {puts "second value"} default {puts "some other value"} }
In this case, both the values second
and 2nd
would match with the second option.
Loops
Tcl offers three commands that allow the iterational execution of code: for, while
, and foreach
.
The following example presents the usage of the for
command:
for {set x 1} {$x < 4} {incr x} {puts "$x execution"}
The first argument is a code snippet executed at the beginning of the loop (in this example, it defines the counter variable x
and initializes it with value 1)
. The second one is a condition that is checked to be logically true before each loop (if the counter is smaller than 4) and the third is the condition which is evaluated after each loop—in most cases, to increase the counter (for example, using the command incr)
. The last part of the statement is the body of the loop. The output of the example is:
The while
command operates in a similar fashion, with the exception that the condition is evaluated after each loop, not before, and there is neither a start nor "after each loop" code section present. The equivalent of the previous example code written with while is:
set x 0 while {$x < 3} {incr x; puts "$x execution"}
The last command is foreach
. The idea behind this command is to pass one (or more) lists, and the loop's body is executed for every element of the list:
The following example shows using two lists at the same time. In every loop, consecutive elements from both lists are assigned to the variables amount
and item:
What is interesting here is that the lists are not required to be of the same length. If one of them is shorter, the empty value is used instead of using missing elements.
foreach
also allows us to fetch multiple variables in one shot—for example, to put all data in a three-column table in HTML, it's just as easy as:
foreach {c1 c2 c3} $data { append html "<tr><td>$c1</td><td>$c2</td><td>$c3</td></tr>" }
Here $data
is a list.
It is possible to control the flow of loops with the commands break
and continue
. Using the first one will cause the loop to exit permanently, even when the end condition is not met. The second command causes the skipping of the current loop and the immediate start of the next one, or the end of the loop if the command that was skipped was the last one.
Defining your own commands
Up until this moment, we have been using only commands delivered by 'someone'. It is time to create our own command for the first time:
proc myCommand {name} { return "Hello $name, have a nice day!" }
We use the proc
command to define a new command (procedure) called myCommand. proc
accepts three arguments: the procedure name, a list of arguments for this procedure, and its body. Note that just like any other argument, these have to be separated by whitespaces. In this case, myCommand
accepts only one argument—name. The implementation is simple, because the command returns some greetings as its execution result. You can use this command in the same way as the others: