Tcl 8.5 Network Programming

3 (2 reviews total)
By Wojciech Kocjan , Piotr Beltowski
  • Instant online access to over 8,000+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Introducing Tcl

About this book

Tcl (Tool Command Language) is a very powerful and easy to learn dynamic programming language, suitable for a very wide range of uses. Tcl is regarded as one of the best-kept secrets in the software industry. This book gives you a hands-on experience on Tcl, helping you develop network-aware applications using this mature yet evolving language.

This book shows you how to create network-aware applications with Tcl language. Packed with practical examples, the book not only takes you through the implementation of network protocols in Tcl, but also key aspects of Tcl programming.

The book starts with the basic element of Tcl programming as we take a look at the syntax and fundamental commands of the language. To get us ready for network programming, we look at important Tcl features such as object-oriented programming, accessing files, packaging in TCL, event driven programming, and multithreaded applications. To create standalone single-file executable applications with Tcl we take a look at the Starpack technology, and ensure that we’ll be able to create robust applications with a thorough coverage of troubleshooting and debugging Tcl applications.

The book is really about network programming, and it will not let you down with its deep coverage of these topics. Of course we look at protocols, but there are plenty of practical examples to keep things moving along. We start with the TCP and UDP protocols, and look at some other protocols to see examples of synchronizing time with other servers, querying user information and authenticating users over LDAP and performing DNS queries.

The book explains Simple Network Management Protocol (SNMP), which is often used for monitoring and gathering information from various devices, such as routers, gateways, printers and many other types of equipment. We’ll also look at web programming in Tcl; processing the requests coming from the clients via the HTTP protocol and responding to these requests. You’ll be able to create a complete solution for creating a client-server application in Tcl.

To round things off, you’ll see how to secure your networked applications, build public key infrastructure into your application and use Tcl’s safe interpreter feature to reduce risk of running code from unknown source.

Publication date:
July 2010
Publisher
Packt
Pages
588
ISBN
9781849510967

 

Chapter 1. Introducing Tcl

The purpose of this chapter is to refresh your memory with some basic and fundamental information about Tcl—what it is, what it is not, and why it was created. It is not intended to make you learn Tcl from scratch or to replace or duplicate the official Tcl documentation—there are a lot of sources, both online and printed. However, it is to briefly summarize facts and allow the reader to easily "tune in" to the topic of the book. If you are new to Tcl, after going through this chapter, you will have an overview of the language and its basics, and it would be a good idea to experiment with it, create your own scripts, and study sophisticated options for the described commands before proceeding to further reading. If you are an experienced Tcl programmer, you can probably skip this chapter.

This chapter discusses the origins of Tcl language—the fundamental idea behind its creation, why it was created, and what kind of problems it addresses. It presents Tcl concepts, describes the possibilities Tcl offers, and explains flexibility that comes from a choice of extensions available for use. We also learn about the license agreement it is distributed under and who is involved in this undertaking.

The chapter also talks about a range of enterprise-level platforms supported by Tcl. Its strength comes from the portability it offers. We briefly describe how to obtain a ready-to-use distribution to start your adventure with Tcl, along with its installation on both Windows and Unix environments.

We also describe the tools you can use to develop your own code. To start doing this, nothing more than a plaintext editor is required. However, you will save lots of time and frustration by using an Integrated Development Environment (IDE). Both Eclipse and Komodo are discussed, along with installation and configuration tips, and the creation and execution of your first script.

After that, we come to Tcl's syntax, giving you the basics of reading the code and creating your first scripts. Once you have read this, you will be able to understand the structure of the code, identify its blocks and particular instructions. You will also get familiar with the basic data types and flow control structures offered by the language.

What is Tcl/Tk

Basically, Tcl is yet another scripting language. It was created by Professor John K. Ousterhout in 1988. Why would someone bother to create their own language? John and his students created a set of tools for designing integrated circuits at the University of California at Berkeley. Many of these tools had their own dedicated command languages, allowing interaction with the user. As the primary focus was on the tools, the command languages were often weakly designed, with odd syntax. With the passing of time, the team realized they were missing an easy-to-use, simple, and embeddable common command language that would allow various tools to have a unified command syntax, and it was only a matter of time before Tcl came into existence.

Consider building a complex application similar to the task of building a house. All you need are bricks of different shapes and functionality, and some kind of substance such as filler or glue to keep it all together. This filler is Tcl, and the bricks are components exposed as commands. What more, you can use bricks (components) provided by someone else (like factory), or you can create your own sophisticated bricks that will do whatever you need them to do, and all these bricks are still joined by the same substance—Tcl! Therefore, an application should be considered as a set of components / modules / "bricks" of different characteristics and purposes, each exposed as a command with standardized syntax and a Tcl script that joins them all. Now when creating your program, you do not have to 'reinvent the wheel' by implementing another to learn the error-prone command language—you can simply embed Tcl inside it. Tcl strongly supports and forces componentization of your application. It allows you to configure and control every application module so that it fits your needs.

Tcl is a scripting language with a UNIX-shell like syntax, where all operations are commands. Yes, let's say that again—it's all about the commands here. Everything in Tcl is a command. It comes with a set of built-in commands that implement basic ideas that every programming language simply has to have, namely variables, flow control, or procedures. But this is where the magic begins, as you will see later in the second chapter, the flexibility of Tcl allows us to easily extend it with object-oriented programming concepts.

Tcl is supported on a wide range of platforms, including Microsoft Windows, Mac OS, Linux, Solaris, HP-UX, AIX and others. You can get a full list of supported platforms at: http://www.tcl.tk/software/tcltk/platforms.html

Tcl is a dynamic scripting language—this means the source code you write using Tcl is not compiled in to binary file that may be executed on its own. On the contrary, it is stored 'as is'—in form of a set of instructions (commands) written in one or more plaintext files, interpreted at runtime by the Tcl interpreter. The interpreter is nothing more than a normal binary application that can understand and process scripts it is provided with. A support for a specific platform means that there is an interpreter for this platform.

Each running Tcl interpreter should be considered as a separate virtual machine having its own internal state that is consequently modified by subsequent commands read from standard input. The commands can also be read from the file, so you can execute your existing script, which will eventually lead the virtual interpreted machine to a new state and then interact with it dynamically.

It is possible to use Tcl entirely in an interactive manner by entering commands manually instead of executing an earlier created script. In order to do this, you simply run the interpreter and enter commands via the simple, standard shell—after the prompt (by default it is % character). This ability adds great flexibility into both experimenting with and rapidly developing the code. This shell application is commonly called tclsh, but for example, in case of the ActiveTcl distribution (described later in this chapter), the name tclsh85 is used to reflect Tcl version it supports. The following screenshot illustrates the shell's start as well as the execution of some basic commands:

The Tcl command info patchlevel returns Tcl's version information and exit terminates the interactive interpreter shell.

As Tcl scripts are nothing more than plaintext files, you do not need any special tools to develop them. All you need is a text editor with which you can create or modify Tcl files. If tclsh is executed with the filename, the shell is not shown, and the interpreter executes the script that is read from that file. The following screenshot illustrates this:

The script file called main.tcl constitutes the entire script in this example. Its content is the very much used "Hello World" example:

#my first Tcl script
puts "Hello World one more time!"

The first line demonstrates how comments are made in Tcl—it starts with the # character, and the second line contains the command put with the string attribute enclosed within quotation marks. This command will send the provided string to standard output.

To execute it, we pass the file to Tcl interpreter tclsh85.

Along with tclsh comes another application called wish, which basically has the same functionality. The main difference is that it preloads the Tk package and, in case of MS Windows, creates a separate window-based shell rather than using the system console.

Early implementations of Tcl interpreters were internally based on string representation of executed script that resulted in sluggish execution and poor speed. In later versions, a "just-in-time" compiler was added, which converts the Tcl script, on the fly, into the byte code language (BCL) internal representation—a concept used in languages such as Java. Such a byte code representation of the script is next executed by the interpreter, which significantly improves its performance. It is also possible to save compiled script for future usage, with the typical extension .tbc. The main benefit to using bytecode files is that the source code is hidden—it does not give any real performance improvements.

Based on what we discussed till now, any Tcl program consists of one or more scripts plus an interpreter. Such a form allows easy and rapid development, meaning that you simply need to modify one of your scripts with text editor, but it also has its own drawbacks. It is much easier to distribute your program as a standalone, ready-to-execute binary file. Distribution of Tcl source files would be really awkward—for example, you may be not able to install the interpreter because of your system administrator's policy. Besides, how many times have you wanted to try out some interesting Java application only to get angry when you realized that to run it, you have to download dozens of megabytes and install Java virtual machine? If you consider this a "not so elegant" solution and are in favor of standalone applications, here is the good news—it is possible to pack all your scripts along with the interpreter into one binary, executable file that is not different from "normal" applications! We will discuss this in more detail in Chapter 3. It is quite possible that you are already an unknowing user of such applications, as there are a number of these available, for example, an excellent MP3 player called SnackAmp (http://snackamp.sourceforge.net) or the encrypted passwords vault Password Gorilla (http://www.fpx.de/fp/Software/Gorilla/).

Tcl language is a standard example of an open source idea. The BSD-style license gives you all the freedom of usage; you can use and modify it in any way you want—also, commercial usage is permitted (detailed license is available at http://www.tcl.tk/software/tcltk/license.html). Both Tcl and Tk are maintained by a world-wide community lead by the group of experts composing Tcl Core Team. Anyone can propose improvements by using the Tcl Improvement Proposal (TIP) feature and sending the proposal to TCT. The community's central site is the Tcl Developer Xchange (http://www.tcl.tk/), which serves as a primary source of Tcl-related information.

One of the most significant contributors is a Canadian Software Company called ActiveState, which specializes in dynamic languages. It provides its own distribution of Tcl called ActiveTcl and also offers various development tools (both of these are discussed later in this chapter).

Across this book, we will present Tcl in its latest available version, which is 8.5 at the time of writing this book. However, majority of information is valid for the version 8.4 and the previous versions. To sum it all up, Tcl is a mature scripting language with a lot of extensions coming from the open source community, and it allows you to quickly develop multi-platform command-line or GUI-based applications.

Extensions

The great flexibility of Tcl comes from its ability to extend the available set of commands known to the interpreter by adding additional extensions, that is, Tcl packages. Packages can be written in pure Tcl, making them platform-independent, or in native code like C/C++, which is then compiled for a specific target platform. It is possible that a package will consist of both of these. There are a lot of packages available on the Internet, both free of use and commercial. A good place to start is Tcllib—Tcl Standard Library. This is a set of extensions written in pure Tcl, available at http://tcllib.sourceforge.net/. Soon you will notice that we will use various packages from this repository for the examples in this book.

What's more interesting is that if you find out that nobody created an extension you would like to use, you will be able to create your own one and share it with the community.

Tk

One of the extensions this chapter will cover is Tk, which was developed by John Osterhout. Tk is a cross-platform GUI framework, which provides the same functions for Tcl as AWT and Swing do for Java. In other words, it is a library that contains a set of typical basic elements (widgets) such as buttons, frames, a canvas, menus, and so on. Tk widgets are flexible in terms of their customization options; almost every aspect may be defined at time of creation or modified later. One of its features is a native "look and feel", which simply means that Tcl/Tk graphical applications do not differ significantly from other native applications in terms of the look and usage. As over the years, Tk appearance started to fall behind the looks of modern operating systems, a number of extensions were created. Also worth mentioning is the Tile Widget Set (http://tktable.sourceforge.net/tile/), which brings re-implementation of some Tk core widget, as well as addition of a few new widgets. Tile is also an engine that allows the usage of themes. According to the Tile documentation:

A theme is a collection of elements and styles that determine the look and feel of the widget set.

Tile has been incorporated into Tk core functionality in version 8.5, which means it is now available in all Tcl/Tk distributions. Although many Tk widgets have corresponding newer Tile equivalents, they can coexist in one application and the developer has the freedom of choice as to which version to use.

As an example, have a look at the following screenshot of SnackAmp that illustrates Tcl's possibilities in the field of GUI applications:

Tk became so popular that it is no longer Tcl-specific. It is now possible to use it in other scripting languages like Perl or Python.

 

What is Tcl/Tk


Basically, Tcl is yet another scripting language. It was created by Professor John K. Ousterhout in 1988. Why would someone bother to create their own language? John and his students created a set of tools for designing integrated circuits at the University of California at Berkeley. Many of these tools had their own dedicated command languages, allowing interaction with the user. As the primary focus was on the tools, the command languages were often weakly designed, with odd syntax. With the passing of time, the team realized they were missing an easy-to-use, simple, and embeddable common command language that would allow various tools to have a unified command syntax, and it was only a matter of time before Tcl came into existence.

Consider building a complex application similar to the task of building a house. All you need are bricks of different shapes and functionality, and some kind of substance such as filler or glue to keep it all together. This filler is Tcl, and the bricks are components exposed as commands. What more, you can use bricks (components) provided by someone else (like factory), or you can create your own sophisticated bricks that will do whatever you need them to do, and all these bricks are still joined by the same substance—Tcl! Therefore, an application should be considered as a set of components / modules / "bricks" of different characteristics and purposes, each exposed as a command with standardized syntax and a Tcl script that joins them all. Now when creating your program, you do not have to 'reinvent the wheel' by implementing another to learn the error-prone command language—you can simply embed Tcl inside it. Tcl strongly supports and forces componentization of your application. It allows you to configure and control every application module so that it fits your needs.

Tcl is a scripting language with a UNIX-shell like syntax, where all operations are commands. Yes, let's say that again—it's all about the commands here. Everything in Tcl is a command. It comes with a set of built-in commands that implement basic ideas that every programming language simply has to have, namely variables, flow control, or procedures. But this is where the magic begins, as you will see later in the second chapter, the flexibility of Tcl allows us to easily extend it with object-oriented programming concepts.

Tcl is supported on a wide range of platforms, including Microsoft Windows, Mac OS, Linux, Solaris, HP-UX, AIX and others. You can get a full list of supported platforms at: http://www.tcl.tk/software/tcltk/platforms.html

Tcl is a dynamic scripting language—this means the source code you write using Tcl is not compiled in to binary file that may be executed on its own. On the contrary, it is stored 'as is'—in form of a set of instructions (commands) written in one or more plaintext files, interpreted at runtime by the Tcl interpreter. The interpreter is nothing more than a normal binary application that can understand and process scripts it is provided with. A support for a specific platform means that there is an interpreter for this platform.

Each running Tcl interpreter should be considered as a separate virtual machine having its own internal state that is consequently modified by subsequent commands read from standard input. The commands can also be read from the file, so you can execute your existing script, which will eventually lead the virtual interpreted machine to a new state and then interact with it dynamically.

It is possible to use Tcl entirely in an interactive manner by entering commands manually instead of executing an earlier created script. In order to do this, you simply run the interpreter and enter commands via the simple, standard shell—after the prompt (by default it is % character). This ability adds great flexibility into both experimenting with and rapidly developing the code. This shell application is commonly called tclsh, but for example, in case of the ActiveTcl distribution (described later in this chapter), the name tclsh85 is used to reflect Tcl version it supports. The following screenshot illustrates the shell's start as well as the execution of some basic commands:

The Tcl command info patchlevel returns Tcl's version information and exit terminates the interactive interpreter shell.

As Tcl scripts are nothing more than plaintext files, you do not need any special tools to develop them. All you need is a text editor with which you can create or modify Tcl files. If tclsh is executed with the filename, the shell is not shown, and the interpreter executes the script that is read from that file. The following screenshot illustrates this:

The script file called main.tcl constitutes the entire script in this example. Its content is the very much used "Hello World" example:

#my first Tcl script
puts "Hello World one more time!"

The first line demonstrates how comments are made in Tcl—it starts with the # character, and the second line contains the command put with the string attribute enclosed within quotation marks. This command will send the provided string to standard output.

To execute it, we pass the file to Tcl interpreter tclsh85.

Along with tclsh comes another application called wish, which basically has the same functionality. The main difference is that it preloads the Tk package and, in case of MS Windows, creates a separate window-based shell rather than using the system console.

Early implementations of Tcl interpreters were internally based on string representation of executed script that resulted in sluggish execution and poor speed. In later versions, a "just-in-time" compiler was added, which converts the Tcl script, on the fly, into the byte code language (BCL) internal representation—a concept used in languages such as Java. Such a byte code representation of the script is next executed by the interpreter, which significantly improves its performance. It is also possible to save compiled script for future usage, with the typical extension .tbc. The main benefit to using bytecode files is that the source code is hidden—it does not give any real performance improvements.

Based on what we discussed till now, any Tcl program consists of one or more scripts plus an interpreter. Such a form allows easy and rapid development, meaning that you simply need to modify one of your scripts with text editor, but it also has its own drawbacks. It is much easier to distribute your program as a standalone, ready-to-execute binary file. Distribution of Tcl source files would be really awkward—for example, you may be not able to install the interpreter because of your system administrator's policy. Besides, how many times have you wanted to try out some interesting Java application only to get angry when you realized that to run it, you have to download dozens of megabytes and install Java virtual machine? If you consider this a "not so elegant" solution and are in favor of standalone applications, here is the good news—it is possible to pack all your scripts along with the interpreter into one binary, executable file that is not different from "normal" applications! We will discuss this in more detail in Chapter 3. It is quite possible that you are already an unknowing user of such applications, as there are a number of these available, for example, an excellent MP3 player called SnackAmp (http://snackamp.sourceforge.net) or the encrypted passwords vault Password Gorilla (http://www.fpx.de/fp/Software/Gorilla/).

Tcl language is a standard example of an open source idea. The BSD-style license gives you all the freedom of usage; you can use and modify it in any way you want—also, commercial usage is permitted (detailed license is available at http://www.tcl.tk/software/tcltk/license.html). Both Tcl and Tk are maintained by a world-wide community lead by the group of experts composing Tcl Core Team. Anyone can propose improvements by using the Tcl Improvement Proposal (TIP) feature and sending the proposal to TCT. The community's central site is the Tcl Developer Xchange (http://www.tcl.tk/), which serves as a primary source of Tcl-related information.

One of the most significant contributors is a Canadian Software Company called ActiveState, which specializes in dynamic languages. It provides its own distribution of Tcl called ActiveTcl and also offers various development tools (both of these are discussed later in this chapter).

Across this book, we will present Tcl in its latest available version, which is 8.5 at the time of writing this book. However, majority of information is valid for the version 8.4 and the previous versions. To sum it all up, Tcl is a mature scripting language with a lot of extensions coming from the open source community, and it allows you to quickly develop multi-platform command-line or GUI-based applications.

Extensions

The great flexibility of Tcl comes from its ability to extend the available set of commands known to the interpreter by adding additional extensions, that is, Tcl packages. Packages can be written in pure Tcl, making them platform-independent, or in native code like C/C++, which is then compiled for a specific target platform. It is possible that a package will consist of both of these. There are a lot of packages available on the Internet, both free of use and commercial. A good place to start is Tcllib—Tcl Standard Library. This is a set of extensions written in pure Tcl, available at http://tcllib.sourceforge.net/. Soon you will notice that we will use various packages from this repository for the examples in this book.

What's more interesting is that if you find out that nobody created an extension you would like to use, you will be able to create your own one and share it with the community.

Tk

One of the extensions this chapter will cover is Tk, which was developed by John Osterhout. Tk is a cross-platform GUI framework, which provides the same functions for Tcl as AWT and Swing do for Java. In other words, it is a library that contains a set of typical basic elements (widgets) such as buttons, frames, a canvas, menus, and so on. Tk widgets are flexible in terms of their customization options; almost every aspect may be defined at time of creation or modified later. One of its features is a native "look and feel", which simply means that Tcl/Tk graphical applications do not differ significantly from other native applications in terms of the look and usage. As over the years, Tk appearance started to fall behind the looks of modern operating systems, a number of extensions were created. Also worth mentioning is the Tile Widget Set (http://tktable.sourceforge.net/tile/), which brings re-implementation of some Tk core widget, as well as addition of a few new widgets. Tile is also an engine that allows the usage of themes. According to the Tile documentation:

A theme is a collection of elements and styles that determine the look and feel of the widget set.

Tile has been incorporated into Tk core functionality in version 8.5, which means it is now available in all Tcl/Tk distributions. Although many Tk widgets have corresponding newer Tile equivalents, they can coexist in one application and the developer has the freedom of choice as to which version to use.

As an example, have a look at the following screenshot of SnackAmp that illustrates Tcl's possibilities in the field of GUI applications:

Tk became so popular that it is no longer Tcl-specific. It is now possible to use it in other scripting languages like Perl or Python.

 

Installation


Source releases for Tcl/Tk along with configuration, compilation, and installation instructions are available at http://www.tcl.tk/software/tcltk/download.html, so everyone can compile the interpreter for themselves. Typically, you don't want to waste your time doing this, especially when someone has done it before, and the fastest and easiest way is to get precompiled binaries for your platform. Among the various distributions of Tcl, ActiveTcl is considered as one of the best. It is available at ActiveState's site http://www.activestate.com/activetcl/. Not only does it contain Tcl and Tk, but it also offers a large number of extensions. The unique strength of the ActiveTcl distribution is that it covers a wide range of enterprise-level operating systems by offering binaries for Windows, Linux, Mac OS X, Solaris, and HP-UX. In this chapter, we will focus on installation under Windows XP and Ubuntu Linux. As almost every Linux distribution comes with its own set of Tcl binaries, users of these systems usually have more choices.

At the time of writing this book, the latest stable release of ActiveTcl is 8.5.7.0.

Windows

In the case of Microsoft Windows, ActiveTcl is substantially the only choice. Installation is simple; the installer is distributed in the form of an executable file (named as something like ActiveTcl8.5.7.0.290198-win32-ix86-threaded.exe). Once you download and run it, you will be presented with a graphical installation wizard similar to the one shown in the following screenshot:

On subsequent screens, you will be presented with a detailed ActiveTcl License. You will also be able to specify installation directory—associate the application with the .tcl extension and configure some of the other details. For the rest of this chapter, we will assume that Tcl is installed in the C:\Tcl directory.

The installer updates system variable PATH with the path to the directory where Tcl binaries are located, that is C:\Tcl\bin. It allows you to execute commands like tclsh85.exe anywhere, without specifying the directory's full path. Note that, as usual, you are advised against using a path containing whitespaces.

Unix-like systems

We will consider Unix-compatible systems using Ubuntu 9.04 Linux as a popular, easy-to-get example. In the case of these systems, you have more choices than you do with Windows. One of the choices you have is to install ActiveTcl (there is a good chance it supports your platform), another is to search for a precompiled Tcl distribution. Ubuntu, for example, comes with a significant number of optional packages of additional software prepared by the community. Among them are of course binary compilations of Tcl. It is possible that your system already has the Tcl distribution installed, but even if not, it is extremely easy to install it, because Ubuntu will be able to suggest what packages should be installed based on the commands that these packages provide for the system. The following is a screenshot of such a situation:

The user wants to execute the tclsh command to get Tcl interactive shell. The package that provides this command is not yet installed, and the system informs the user what packages are available. In this case, we have decided to choose tcl8.5 and install it using the command-line command as follows:

sudo apt-get install tcl8.5

Once installed, you can run the tclsh command to get the interactive Tcl shell as follows:

To verify the detailed version of Tcl interpreter which you have just installed, use the info patchlevel command.

Tk comes in a separate Ubuntu package. You will be notified of this if you attempt to run an application that requires it, for example wish. Similarly, it can be installed with:

% sudo apt-get install tk8.5.

Installation of additional Tcl packages

Just like core Tcl installation, extensions, that is Tcl packages, can also be installed in two ways: the first is to get the source code of an extension and build (compile) it, and the second one is to find, download, and install a pre-built package.

In order to be able to use the extension in your script, it must be available for the interpreter. There are several locations where Tcl can search for it, and these locations are kept in the special Tcl variable called auto_path. You can learn its value by starting the Tcl shell and executing the following command:

puts $auto_path

The puts command prints the content of the specified variable to standard output. In the case of Windows, the output appears as:

Now that you know this, you can put the packages you require under the C:\Tcl\lib location to make them available for the interpreter. The concept of the $auto_path variable and other packages will be described in more detail in the next chapter.

ActiveTcl comes with a handy utility called Teacup. This utility allows for easy installation of prebuilt Tcl extensions from external remote repositories called Tcl Extension Archives. These archives, also known as Teapot, are centrally managed sites. Currently, there is one public Teapot maintained by ActiveState. By using Teacup, you can install the package you need into your local repository and then use it in your scripts. It will automatically solve dependencies, localize package(s), and download and install them.

 

Additional tools


In these days of rapid application development, using plaintext editors like notepad or vi can be really inconvenient and time consuming. Luckily, there is far better alternative— an Integrated Development Environment (IDE). IDEs are tools that allow efficient code creation and editing, support testing your code from within the editor, and usually also offer debugging capabilities.

At the time of writing this book, there are two IDEs worth mentioning: Eclipse and Komodo. Both support syntax highlighting and coloring, auto completion, creating launch configurations, managing projects and dependencies, and have many other useful features.

Eclipse

It is hardly possible that you have not heard about Eclipse nowadays. An open source IDE supporting a wide variety of languages, extensible with plugins is considered as one of the best programming environments, and it is all free. Due to the fact that Eclipse was developed in Java, the CPU/memory requirements may be slightly higher than in case of native applications, but in return, it is available for the Windows, Linux, and Mac platforms.

First you have to install the Java Runtime Environment (JRE), we strongly suggest installing latest version available (at the moment it is version 6 Update 17), you can get it from http://java.com. We will discuss Eclipse Classic 3.5.0 available for download at http://www.eclipse.org/downloads/. The installation process is simple—just download and unzip the file to a location of your choice and run eclipse.exe. The full description of Eclipse is out of scope of this book, we will present only the necessary steps to get it running for Tcl development purposes.

By default, Eclipse supports only Java development, so we have to install an additional extension called the Dynamic Languages Toolkit (DLTK) for Tcl support. In order to do this it, you have to navigate to the Help | Install New Software… menu. A new window will show up, as shown in the following screenshot:

First, select the Galileo site from the list at the top. Eclipse will download a list of all available extensions from this site; this may take a while. Once the list is visible, expand Programming Languages and select Dynamic Languages Toolkit TCL Development Tools. Once you click on Next, Eclipse will automatically resolve dependencies and select all the necessary plugins to install. In this case, it will choose to install the DLTK Core and Eclipse Modelling Framework (EMF) extensions, as shown in the following screenshot:

Following this, Eclipse will download and install all the necessary files, and ask you to restart. Once it is done, you have to configure installed Tcl interpreters. Eclipse requires at least one interpreter in order to be able to run scripts. To configure it, navigate to Window | Preferences to open the preferences dialog, and then go to Tcl | Interpreters. Click the Add… button on this pane, and then Browse… and navigate to the interpreter binary, which is installed at C:\Tcl\bin\tclsh85.exe. Once done, you should have configured the default interpreter like here, as shown in the following screenshot:

Now you have your Eclipse fully configured to create and run Tcl scripts! To get a taste of working with Eclipse, let's create the same Hello World example. Navigate to File | New | Project… and choose Tcl Project from the Tcl category and then create a project named TclHelloWorld—it will be shown in the Script Explorer view (by default, on the left of the Eclipse window). The next step is to right-click on the project name in this view and select Tcl File, and name it main.tcl. As a template, select Tcl File. Eclipse will create the file with a specific header that we will explain later. Add the puts command to that script, just as you did in the previous example. Now it is time to execute the script—right-click on it and select Run As | Tcl Script. The output of this execution will be visible in the Console view. The following screen illustrates it:

Komodo

Komodo, available for download at http://www.activestate.com/komodo, comes in two flavors: free Komodo Edit and paid Komodo IDE. Both support a wide range of scripting languages, including PHP, Ruby, Python, and of course Tcl. The first one is a simple editor with syntax checking and coloring, and also Project Management abilities. The second one extends the offered functionality as it comes with a debugger.

Similar to Eclipse, you can create a project (using File | New | New Project…) and a file inside it (right-click on project name in the left pane and select Add | New file…). The new file is created based on a language-specific template, which comes with the universal header described later in this chapter.

To run the script, navigate to Tools | Run Command… and enter tclsh85 %F in the Run dialog, and then click on the Run button.

tkcon an alternate Tcl shell

If you find the standard Tcl shell too primitive, you must definitely have a look at tkcon—a standard console replacement with many useful features including a command history and syntax highlighting. What is even more interesting is that it is implemented as another Tcl package, and therefore it is perfectly possible to embed tkcon in to your Tcl application. The following is a screenshot of tkcon console, with the puts $auto_path command's output:

Tkcon is, by default, included in the ActiveTcl distribution. To install it on Ubuntu, enter:

% sudo apt-get install tkcon .

In order to run it, simply execute the command tkcon.

 

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" [email protected]"
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

[chars]

Matches exactly one character, only that specified inside [] brackets. It is possible to use the [a-z] syntax to match all characters in this range, in this case from 'a' to 'z

\

Allows escaping of any of the special characters, such as ?, *, [ or] and treats them as normal characters to be match with

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

-ascii

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}

-integer

Compares elements as integer values

List: {9 10 11 2} Sorted: {2 9 10 11}

-real

Compares elements as floating point values

List: {11.0 2.0 9 1.2 1}

Sorted: {1 1.2 2.0 9 11.0}

-dictionary

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 the string 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:

 

Summary


Tcl is a mature language and it is easy to use. It covers a wide range of domains, ranging from task automation to fully-featured GUI applications. Its syntax is a bit different from other languages, but once mastered, it becomes clear and logical. The wide set of available extensions along with the ability to define your own commands means there are virtually no limits in terms of extending the capabilities it offers. This fact combined with its support for various platforms makes Tcl a truly swiss-army-knife equivalent among programming languages. Often considered as 'just' another scripting language, trifled and underestimated, Tcl may really surprise you with its abilities and potential.

In this chapter, we have learned:

  • The origins, concepts, and possibilities provided by the Tcl language

  • Which platforms it supports, and how to get and install the Tcl interpreter

  • About some of the tools available to ease the development of Tcl code

  • The syntax and fundamental commands of the language

Having read this chapter, you are now ready to continue to the next one, which will teach you some more advanced Tcl features. Not only will you learn about the many technical details of the interpreter itself, but you will also become familiar with file operations, the packaging system, event loops, and threads.

About the Authors

  • Wojciech Kocjan

    Wojciech Kocjan is a system administrator and programmer with 10 years of experience. His work experience includes several years of using Nagios for enterprise IT infrastructure monitoring. He also has experience with wide variety of devices and servers, routers, Linux, Solaris, AIX servers and i5/OS mainframes. His programming experience includes multiple languages (such as Java, Ruby, Python, and Perl) and focuses on web applications as well as client-server solutions.

    Browse publications by this author
  • Piotr Beltowski

    Piotr Beltowski is an IT and software engineer, and co-author of several US patents and publications. A Master of Science in telecommunications, his technical experience includes various aspects of telecommunication, such as designing IP networks or implementing support for network protocols. Piotr's experience as Level 3 customer support lead makes him focused on finding simple solutions to complex technical issues.

    His professional career includes working in international corporations such as IBM and Ericsson.

    Browse publications by this author

Latest Reviews

(2 reviews total)
I bought a book weeks ago and it still isn't here.
Excellent

Recommended For You

Book Title
Access this book, plus 8,000 other titles for FREE
Access now