Practical Programming in TCL and TK
Practical Programming in TCL and TK
By BrentB.Welch
Why Tcl?
On-line Examples
Ftp Archives
Newsgroups
Typographic Conventions
Hot Tips
Book Organization
Tcl Commands
Hello, World!
Variables
Command Substitution
Math Expressions
Backslash Substitution
Procedures
A Factorial Example
Comments
Fine Points
Reference
Command-Line Arguments
Predefined Variables
Next Steps
Related Chapters
Tcl Lists
Constructing Lists
Related Chapters
If Then Else
Switch
While
Foreach
For
Catch
Error
Return
Scope
Array Syntax
File Attributes
Environment Variables
Syntax Summary
Using Packages
Interactive Conveniences
Coding Style
Cross-Platform Support
Debugging
Scriptics' TclPro
Other Tools
Performance Tuning
Chapter14. Namespaces
Using Namespaces
Namespace Variables
Command Lookup
Nested Namespaces
Introspection
Notes
Chapter15. Internationalization
Message Catalogs
Client Sockets
Server Sockets
Basic Authentication
Domain Handlers
Document Types
Form Handlers
Programming Reference
Server Configuration
Creating Interpreters
Safe Interpreters
Command Aliases
Hidden Commands
Substitutions
Security Policies
Tk in Child Interpreters
Chapter21. Tk Fundamentals
Hello, World! in Tk
Naming Tk Widgets
Configuring Tk Widgets
Chapter22. Tk by Example
ExecLog
A Tcl Shell
Anchoring
Packing Order
Unpacking a Widget
Packer Summary
A Basic Grid
place Basics
Event Syntax
Modifiers
Event Sequences
Virtual Events
Event Keywords
PartIV. Tk Widgets
Button Attributes
Button Operations
Keyboard Traversal
Menu Attributes
An Introduction to Resources
User-Defined Buttons
User-Defined Menus
Chapter30. Scrollbars
Using Scrollbars
Using Listboxes
Listbox Bindings
Listbox Attributes
Text Indices
Text Marks
Text Tags
The Selection
Tag Bindings
Searching Text
Embedded Widgets
Embedded Images
Text Bindings
Text Operations
Text Attributes
Canvas Coordinates
Hello, World!
Canvas Objects
Canvas Operations
Generating Postscript
Canvas Attributes
Hints
PartV. Tk Details
Selection Handlers
Standard Dialogs
Custom Dialogs
Configuring Attributes
Size
Colors
Naming a Font
X Font Names
Font Metrics
Text Attributes
Chapter40. Send
Communicating Processes
The wm Command
The tk Command
App-Defaults Files
Defining Preferences
Basic Concepts
A C Command Procedure
Using autoconf
Final Cleanup
wish
Obsolete Features
Bindings
Scrollbar Interface
pack info
Focus
Radiobutton Value
Entry Widget
Menus
Listboxes
No geometry Attribute
Text Widget
Color Attributes
Canvas scrollincrement
The Selection
Cross-Platform Scripts
Network Sockets
Virtual Events
Standard Dialogs
Namespaces
Safe-Tcl
New lsort
tcl_precision Variable
Http Package
Platform-Independent Fonts
Application Embedding
grid rowconfigure
Thread Safety
Miscellaneous
Proposed Tk Changes
Technical Support
Index
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Copyright
Library of Congress Cataloging-in-Publication Data
Welch, Brent. B.
Practical programming in Tcl and Tk / Brent B. Welch.-- 3rd ed.
p. cm.
ISBN 0-13-022028-0
1. Tcl (Computer program language) 2. Tk toolkit. I. Title.
QA76.73.T44 W45 1999
005.13'3--dc21 99-047206
Credits
Editorial/Production Supervision: Joan L. McNamara
Acquisitions Editor: Mark Taub
Marketing Manager: Kate Hargett
Editorial Assistant: Michael Fredette
Cover Design Director: Jerry Votta
Cover Design: Design Source
Manufacturing Manager: Alexis R. Heydt
2000, 1997 by Prentice Hall PTR
Prentice-Hall, Inc.
Upper Saddle River, New Jersey 07458
Prentice Hall books are widely used by corporations and government agencies for training, marketing,
and resale. The publisher offers discounts on this book when ordered in bulk quantities. For more
information, contact:
Corporate Sales Department, Prentice Hall PTR, One Lake Street, Upper Saddle River, NJ 07458
Phone: 800-382-3419; Fax: 201-236-7141; email: [email protected]
All rights reserved. No part of this book may be reproduced, in any form or by any means, without
permission in writing from the publisher.
All product names mentioned herein are the trademarks of their respective owners.
Printed in the United States of America
10 9 8 7 6 5 4 3 2 1
Prentice-Hall International (UK) Limited, London
Prentice-Hall of Australia Pty. Limited, Sydney
Prentice-Hall Canada Inc., Toronto
Prentice-Hall Hispanoamericana, S.A., Mexico
Prentice-Hall of India Private Limited, New Delhi
Prentice-Hall of Japan, Inc., Tokyo
Prentice-Hall (Singapore) Pte. Ltd., Singapore
Editora Prentice-Hall do Brasil, Ltda., Rio de Janeiro
Dedication
to Jody, Christopher, Daniel, and Michael
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
List of Examples
1.1 The "Hello, World!" example
1.2 Tcl variables
1.3 Command substitution
1.4 Simple arithmetic
1.5 Nested commands
1.6 Built-in math functions
1.7 Grouping expressions with braces
1.8 Quoting special characters with backslash
1.9 Continuing long lines with backslashes
1.10 Grouping with double quotes vs. braces
1.11 Embedded command and variable substitution
1.12 Defining a procedure
1.13 A while loop to compute factorial
1.14 A recursive definition of factorial
1.15 Using set to return a variable value
1.16 Embedded variable references
1.17 Using info to determine if a variable exists
1.18 Controlling precision with tcl_precision
2.1 A standalone Tcl script on UNIX
and the aspects of C programming to create Tcl extensions is given a lighter treatment. I have been
lucky to remain involved in the core Tcl development, and I hope I can pass along the insights I have
gained by working with Tcl.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Why Tcl?
As a scripting language, Tcl is similar to other UNIX shell languages such as the Bourne Shell (sh),
the C Shell (csh), the Korn Shell (ksh), and Perl. Shell programs let you execute other programs. They
provide enough programmability (variables, control flow, and procedures) to let you build complex
scripts that assemble existing programs into a new tool tailored for your needs. Shells are wonderful
for automating routine chores.
It is the ability to easily add a Tcl interpreter to your application that sets it apart from other shells. Tcl
fills the role of an extension language that is used to configure and customize applications. There is no
need to invent a command language for your new application, or struggle to provide some sort of user-
programmability for your tool. Instead, by adding a Tcl interpreter, you structure your application as a
set of primitive operations that can be composed by a script to best suit the needs of your users. It also
allows other programs to have programmatic control over your application, leading to suites of
applications that work well together.
The Tcl C library has clean interfaces and is simple to use. The library implements the basic interpreter
and a set of core scripting commands that implement variables, flow control, and procedures (see page
22). There is also a set of commands that access operating system services to run other programs,
access the file system, and use network sockets. Tk adds commands to create graphical user interfaces.
Tcl and Tk provide a "virtual machine" that is portable across UNIX, Windows, and Macintosh
environments.
The Tcl virtual machine is extensible because your application can define new Tcl commands. These
commands are associated with a C or C++ procedure that your application provides. The result is
applications that are split into a set of primitives written in a compiled language and exported as Tcl
commands. A Tcl script is used to compose the primitives into the overall application. The script layer
has access to shell-like capability to run other programs, has access to the file system, and can call
directly into the compiled part of the application through the Tcl commands you define. In addition,
from the C programming level, you can call Tcl scripts, set and query Tcl variables, and even trace the
execution of the Tcl interpreter.
There are many Tcl extensions freely available on the Internet. Most extensions include a C library
that provides some new functionality, and a Tcl interface to the library. Examples include database
access, telephone control, MIDI controller access, and expect, which adds Tcl commands to control
interactive programs.
The most notable extension is Tk, a toolkit for graphical user interfaces. Tk defines Tcl commands
that let you create and manipulate user interface widgets. The script-based approach to user interface
programming has three benefits:
Development is fast because of the rapid turnaround; there is no waiting for long compilations.
The Tcl commands provide a higher-level interface than most standard C library user-interface
toolkits. Simple user interfaces require just a handful of commands to define them. At the same
time, it is possible to refine the user interface in order to get every detail just so. The fast
turnaround aids the refinement process.
The user interface can be factored out from the rest of your application. The developer can
concentrate on the implementation of the application core and then fairly painlessly work up a
user interface. The core set of Tk widgets is often sufficient for all your user interface needs.
However, it is also possible to write custom Tk widgets in C, and again there are many
contributed Tk widgets available on the network.
There are other choices for extension languages that include Visual Basic, Scheme, Elisp, Perl,
Python, and Javascript. Your choice between them is partly a matter of taste. Tcl has simple constructs
and looks somewhat like C. It is easy to add new Tcl primitives by writing C procedures. Tcl is very
easy to learn, and I have heard many great stories of users completing impressive projects in a short
amount of time (e.g., a few weeks), even though they never used Tcl before.
Java has exploded onto the computer scene since this book was first published. Java is a great systems
programming language that in the long run could displace C and C++. This is fine for Tcl, which is
designed to glue together building blocks written in any system programming language. Tcl was
designed to work with C, but has been adapted to work with the Java Virtual Machine. Where I say "C
or C++", you can now say "C, C++, or Java," but the details are a bit different with Java. This book
does not describe the Tcl/Java interface, but you can find TclBlend on the CD-ROM. TclBlend loads
the Java Virtual Machine into your Tcl application and lets you invoke Java methods. It also lets you
implement Tcl commands in Java instead of C or C++.
Javascript is a language from Netscape that is designed to script interactions with Web pages.
Javascript is important because Netscape is widely deployed. However, Tcl provides a more general
purpose scripting solution that can be used in a wide variety of applications. The Tcl/Tk Web browser
plugin provides a way to run Tcl in your browser. It turns out to be more of a Java alternative than a
JavaScript alternative. The plugin lets you run Tcl applications inside your browser, while JavaScript
gives you fine grain control over the browser and HTML display. The plugin is described in Chapter
20.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Tcl and Tk Versions
Tcl and Tk continue to evolve. See http://www.beedub.com/book/ for updates and news about the
latest Tcl releases. Tcl and Tk have had separate version numbers for historical reasons, but they are
released in pairs that work together. The original edition of this book was based on Tcl 7.4 and Tk 4.0,
and there were a few references to features in Tk 3.6. This third edition has been updated to reflect
new features added through Tcl/Tk 8.2:
Tcl 7.5 and Tk 4.1 had their final release in May 1996. These releases feature the port of Tk to
the Windows and Macintosh environments. The Safe-Tcl security mechanism was introduced to
support safe execution of network applets. There is also network socket support and a new
Input/Output (I/O) subsystem to support high-performance event-driven I/O.
Tcl 7.6 and Tk 4.2 had their final release in October 1996. These releases include improvements
in Safe-Tcl, and improvements to the grid geometry manager introduced in Tk 4.1. Cross-
platform support includes virtual events (e.g., <<Copy>> as opposed to <Control-c>), standard
dialogs, and more file manipulation commands.
Tcl 7.7 and Tk 4.3 were internal releases used for the development of the Tcl/Tk plug-in for the
Netscape Navigator and Microsoft Internet Explorer Web browsers. Their development actually
proceeded in parallel to Tcl 7.6 and Tk 4.2. The plug-in has been released for a wide variety of
platforms, including Solaris/SPARC, Solaris/INTEL, SunOS, Linux, Digital UNIX, IRIX,
HP/UX, Windows 95, Windows NT, and the Macintosh. The browser plug-in supports Tcl
applets in Web pages and uses the sophisticated security mechanism of Safe-Tcl to provide
safety.
Tcl 8.0 features an on-the-fly compiler for Tcl that provides many-times faster Tcl scripts. Tcl
8.0 supports strings with embedded null characters. The compiler is transparent to Tcl scripts,
but extension writers need to learn some new C APIs to take advantage of its potential. The
release history of 8.0 spread out over a couple of years as John Ousterhout moved from Sun
Microsystems to Scriptics Corporation. The widely used 8.0p2 release was made in the fall of
1997, but the final patch release, 8.0.5, was made in the spring of 1999.
Tk changed its version to match Tcl at 8.0. Tk 8.0 includes a new platform-independent font
mechanism, native menus and menu bars, and more native widgets for better native look and feel
on Windows and Macintosh.
Tcl/Tk 8.1 features full Unicode support, a new regular expression engine that provides all the
features found in Perl 5, and thread safety so that you can embed Tcl into multithreaded
applications. Tk does a heroic job of finding the correct font to display your Unicode characters,
and it adds a message catalog facility so that you can write internationalized applications. The
release history of Tcl/Tk 8.1 also straddled the Sun to Scriptics transition. The first alpha release
was made in the fall of 1997, and the final patch release, 8.1.1, was made in May 1999.
Tcl/Tk 8.2 is primarily a bug fix and stabilization release. There are a few minor additions to the
Tcl C library APIs to support more extensions without requiring core patches. Tcl/Tk 8.2 went
rapidly into final release in the summer of 1999.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Who Should Read This Book
This book is meant to be useful to the beginner in Tcl as well as the expert. For the beginner and
expert alike, I recommend careful study of Chapter 1, Tcl Fundamentals. The programming model of
T0cl is designed to be simple, but it is different from many programming languages. The model is
based on string substitutions, and it is important that you understand it properly to avoid trouble in
complex cases. The remainder of the book consists of examples that demonstrate how to use Tcl and
Tk productively. For your reference, each chapter has tables that summarize the Tcl commands and Tk
widgets they describe.
This book assumes that you have some programming experience, although you should be able to get
by even if you are a complete novice. Knowledge of UNIX shell programming will help, but it is not
required. Where aspects of window systems are relevant, I provide some background information.
Chapter 2 describes the details of using Tcl and Tk on UNIX, Windows, and Macintosh.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
How to Read This Book
This book is best used in a hands-on manner, trying the examples at the computer. The book tries to
fill the gap between the terse Tcl and Tk manual pages, which are complete but lack context and
examples, and existing Tcl programs that may or may not be documented or well written.
I recommend the on-line manual pages for the Tcl and Tk commands. They provide a detailed
reference guide to each command. This book summarizes much of the information from the manual
pages, but it does not provide the complete details, which can vary from release to release. HTML
versions of the on-line manual pages can be found on the CD-ROM that comes with this book.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Other Tcl Books
This book was the second Tcl book after the original book by John Ousterhout, the creator of Tcl.
Since then, the number of Tcl books has increased remarkably. The following are just some of the
books currently available.
Tcl and the Tk Toolkit (Addison-Wesley, 1994) by John Ousterhout provides a broad overview of all
aspects of Tcl and Tk, even though it covers only Tcl 7.3 and Tk 3.6. The book provides a more
detailed treatment of C programming for Tcl extensions.
Exploring Expect (O'Reilly & Associates, Inc., 1995) by Don Libes is a great book about an extremely
useful Tcl extension. Expect lets you automate the use of interactive programs like ftp and telnet that
expect to interact with a user. By combining expect and Tk, you can create graphical user interfaces
for old applications that you cannot modify directly.
Graphical Applications with Tcl & Tk (M&T Press, 1996) by Eric Johnson is oriented toward
Windows users. The second edition is up-to-date with Tcl/Tk 8.0.
Tcl/Tk Tools (O'Reilly & Associates, Inc., 1997) by Mark Harrison describes many useful Tcl
extensions. These include Oracle and Sybase interfaces, object-oriented language enhancements,
additional Tk widgets, and much more. The chapters were contributed by the authors of the
extensions, so they provide authoritative information on some excellent additions to the Tcl toolbox.
CGI Developers Resource, Web Programming with Tcl and Perl (Prentice Hall, 1997) by John Ivler
presents Tcl-based solutions to programming Web sites.
Effective Tcl/Tk Programming (Addison Wesley, 1997) by Michael McLennan and Mark Harrison
illustrate Tcl and Tk with examples and application design guidelines.
Interactive Web Applications with Tcl/Tk (AP Professional, 1998) by Michael Doyle and Hattie
Schroeder describes Tcl programming in the context of the Web browser plugin.
Tcl/Tk for Programmers (IEEE Computer Society, 1998) by Adrian Zimmer describes Unix and
Windows programming with Tcl/Tk. This book also includes solved exercises at the end of each
chapter.
Tcl/Tk for Real Programmers (Academic Press, 1999) by Clif Flynt is another example-oriented book.
Tcl/Tk in a Nutshell (O'Reilly, 1999) by Paul Raines and Jeff Tranter is a handy reference guide. It
covers several popular extensions including Expect, [incr Tcl], Tix, TclX, BLT, SybTcl, OraTcl, and
TclODBC. There is a tiny pocket-reference guide for Tcl/Tk that may eliminate the need to thumb
through my large book to find the syntax of a particular Tcl or Tk command.
Web Tcl Complete (McGraw Hill, 1999) by Steve Ball describes programming with the Tcl Web
Server. It also covers Tcl/Java integration using TclBlend.
[incr Tcl] From The Ground Up (Osborn-McGraw Hill, 1999) by Chad Smith describes the [incr Tcl]
object-oriented extension to Tcl.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
On-line Examples
The book comes with a CD-ROM that has source code for all of the examples, plus a selection of Tcl
freeware found on the Internet. The CD-ROM is created with the Linux mkhybrid program, so it is
readable on UNIX, Windows, and Macintosh. There, you will find the versions of Tcl and Tk that
were available as the book went to press. You can also retrieve the sources shown in the book from my
personal Web site:
http://www.beedub.com/book/
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Ftp Archives
The primary site for the Tcl and Tk distributions is given below as a Universal Resource Location
(URL):
ftp://ftp.scriptics.com/pub/tcl
You can use FTP and log in to the host (e.g., ftp.scriptics.com) under the anonymous user name. Give
your e-mail address as the password. The directory is in the URL after the host name (e.g., /pub/tcl).
There are many sites that mirror this distribution. The mirror sites provide an archive site for
contributed Tcl commands, Tk widgets, and applications. There is also a set of Frequently Asked
Questions files. These are some of the sites that maintain Tcl archives
ftp://ftp.neosoft.com/pub/tcl
ftp://ftp.syd.dit.csiro.au/pub/tk
ftp://ftp.ibp.fr/pub/tcl
ftp://src.doc.ic.ac.uk/packages/tcl/
ftp://ftp.luth.se/pub/unix/tcl/
ftp://sunsite.cnlab-switch.ch/mirror/tcl
ftp://ftp.sterling.com/programming/languages/tcl
ftp://ftp.sunet.se/pub/lang/tcl
ftp://ftp.cs.columbia.edu/archives/tcl
ftp://ftp.uni-paderborn.de/pub/unix/tcl
ftp://sunsite.unc.edu/pub/languages/tcl
ftp://ftp.funet.fi/pub/languages/tcl
You can use a World Wide Web browser like Mosaic, Netscape, Internet Explorer, or Lynx to access
these sites. Enter the URL as specified above, and you are presented with a directory listing of that
location. From there you can change directories and fetch files.
If you do not have direct FTP access, you can use an e-mail server for FTP. Send e-mail to
[email protected] with the message Help to get directions. If you are on BITNET, send e-mail
to [email protected].
You can search for FTP sites that have Tcl by using the Archie service that indexes the contents of
anonymous FTP servers. Information about using Archie can be obtained by sending mail to
[email protected] that contains the message Help.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
World Wide Web
Start with these World Wide Web pages about Tcl:
http://www.scriptics.com/
http://www.sco.com/Technology/tcl/Tcl.html
http://www.purl.org/NET/Tcl-FAQ/
The home page for this book contains errata for all editions. This is the only URL I control personally,
and I plan to keep it up-to-date indefinitely:
http://www.beedub.com/book/
The Prentice Hall Web site has information about the book, but you must use its search facility to find
the exact location. Start at:
http://www.prenhall.com/
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Newsgroups
The comp.lang.tcl newsgroup is very active. It provides a forum for questions and answers about
Tcl. Announcements about Tcl extensions and applications are posted to the
comp.lang.tcl.announce newsgroup.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Typographic Conventions
The more important examples are set apart with a title and horizontal rules, while others appear in-
line. The examples use courier for Tcl and C code. When interesting results are returned by a Tcl
command, those are presented below in oblique courier. The => is not part of the return value in the
following example.
expr 5 + 8
=> 13
The courier font is also used when naming Tcl commands and C procedures within sentences.
The usage of a Tcl command is presented in the following example. The command name and constant
keywords appear in courier. Variable values appear in courier oblique. Optional arguments are
surrounded with question marks.
set varname ?value?
The name of a program is in italics:
xterm
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Hot Tips
The icon in the margin marks a "hot tip" as judged by the reviewers of the book.
The visual markers help you locate the more useful sections in the book. These
are also listed in the index under Hot Tip.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Book Organization
The chapters of the book are divided into seven parts. The first part describes basic Tcl features. The
first chapter describes the fundamental mechanisms that characterize the Tcl language. This is an
important chapter that provides the basic grounding you will need to use Tcl effectively. Even if you
have programmed in Tcl already, you should review Chapter 1. Chapter 2 goes over the details of
using Tcl and Tk on UNIX, Windows, and Macintosh. Chapter 3 presents a sample application, a CGI
script, that illustrates typical Tcl programming. The rest of Part I covers the basic Tcl commands in
more detail, including string handling, data types, control flow, procedures, and scoping issues. Part I
finishes with a description of the facilities for file I/O and running other programs.
Part II describes advanced Tcl programming. It starts with eval, which lets you generate Tcl programs
on the fly. Regular expressions provide powerful string processing. If your data-processing application
runs slowly, you can probably boost its performance significantly with the regular expression facilities.
Namespaces partition the global scope of procedures and variables. Unicode and message catalogs
support internationalized applications. Libraries and packages provide a way to organize your code for
sharing among projects. The introspection facilities of Tcl tell you about the internal state of Tcl.
Event driven I/O helps server applications manage several clients simultaneously. Network sockets are
used to implement the HTTP protocol used to fetch pages on the World Wide Web. Safe-Tcl is used to
provide a secure environment to execute applets downloaded over the network. TclHttpd is an
extensible web server built in Tcl. You can build applications on top of this server, or embed it into
your existing applications to give them a web interface.
Part III introduces Tk. It gives an overview of the toolkit facilities. A few complete examples are
examined in detail to illustrate the features of Tk. Event bindings associate Tcl commands with events
like keystrokes and button clicks. Part III ends with three chapters on the Tk geometry managers that
provide powerful facilities for organizing your user interface.
Part IV describes the Tk widgets. These include buttons, menus, scrollbars, labels, text entries,
multiline and multifont text areas, drawing canvases, listboxes, and scales. The Tk widgets are highly
configurable and very programmable, but their default behaviors make them easy to use as well. The
resource database that can configure widgets provides an easy way to control the overall look of your
application.
Part V describes the rest of the Tk facilities. These include selections, keyboard focus, and standard
dialogs. Fonts, colors, images, and other attributes that are common to the Tk widgets are described in
detail. This part ends with a few larger Tk examples.
Part VI is an introduction to C programming and Tcl. The goal of this part is to get you started in the
right direction when you need to extend Tcl with new commands written in C or integrate Tcl into
custom applications.
Part VII provides a chapter for each of the Tcl/Tk releases covered by the book. These chapters
provide details about what features were changed and added. They also provide a quick reference if
you need to update a program or start to use a new version.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
What's New in the Third Edition
The third edition is up-to-date with Tcl/Tk 8.2. The main new Tcl/Tk features are Internationalization,
which is covered in Chapter 15, a new regular expression engine, which is described in Chapter 11,
and thread-safety. There is a new chapter about compiling C extensions, and there is a more complete
C extension example. The chapters on Eval and the Web browser plugin received a thorough update. I
made a light sweep through the remainder of the book correcting errors and improving examples.
Perhaps the best addition for the reader is an all-new index.
My favorite addition to the book is Chapter 18 that describes TclHttpd, a Web server built in Tcl.
TclHttpd provides a number of nice ways to integrate a Web server with a Tcl application, replacing
the standard CGI interface with something that is much more flexible and efficient. I have been using
this server for the last year to build www.scriptics.com. This freely available server has been used to
build several other products, plus it provides an easy way for you to bring up your own Web server.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
First Edition Thanks
I would like to thank my managers and colleagues at Xerox PARC for their patience with me as I
worked on this book. The tips and tricks in this book came partly from my own work as I helped lab
members use Tcl, and partly from them as they taught me. Dave Nichols' probing questions forced me
to understand the basic mechanisms of the Tcl interpreter. Dan Swinehart and Lawrence Butcher kept
me sharp with their own critiques. Ron Frederick and Berry Kerchival adopted Tk for their graphical
interfaces and amazed me with their rapid results. Becky Burwell, Rich Gold, Carl Hauser, John
Maxwell, Ken Pier, Marvin Theimer, and Mohan Vishwanath made use of my early drafts, and their
questions pointed out large holes in the text. Karin Petersen, Bill Schilit, and Terri Watson kept life
interesting by using Tcl in very nonstandard ways. I especially thank my managers, Mark Weiser and
Doug Terry, for their understanding and support.
I thank John Ousterhout for Tcl and Tk, which are wonderful systems built with excellent
craftsmanship. John was kind enough to provide me with an advance version of Tk 4.0 so that I could
learn about its new features well before its first beta release.
Thanks to the Tcl programmers out on the Net, from whom I learned many tricks. John LoVerso and
Stephen Uhler are the hottest Tcl programmers I know.
Many thanks to the patient reviewers of early drafts: Pierre David, Clif Flynt, Simon Kenyon, Eugene
Lee, Don Libes, Lee Moore, Joe Moss, Hador Shemtov, Frank Stajano, Charles Thayer, and Jim
Thornton.
Many folks contributed suggestions by e-mail: Miguel Angel, Stephen Bensen, Jeff Blaine, Tom
Charnock, Brian Cooper, Patrick D'Cruze, Benoit Desrosiers, Ted Dunning, Mark Eichin, Paul
Friberg, Carl Gauthier, David Gerdes, Klaus Hackenberg, Torkle Hasle, Marti Hearst, Jean-Pierre
Herbert, Jamie Honan, Norman Klein, Joe Konstan, Susan Larson, Hkan Liljegren, Lionel Mallet,
Dejan Milojicic, Greg Minshall, Bernd Mohr, Will Morse, Heiko Nardmann, Gerd Neugebauer, TV
Raman, Cary Renzema, Rob Riepel, Dan Schenk, Jean-Guy Schneider, Elizabeth Scholl, Karl
Schwamb, Rony Shapiro, Peter Simanyi, Vince Skahan, Bill Stumbo, Glen Vanderburg, Larry Virden,
Reed Wade, and Jim Wight. Unfortunately, I could not respond to every suggestion, even some that
were excellent.
Thanks to the editors and staff at Prentice Hall. Mark Taub has been very helpful as I progressed
through my first book. Lynn Schneider and Kerry Reardon were excellent copy and production editors,
respectively.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Second Edition Thanks
I get to thank John Ousterhout again, this time for supporting me as I worked in the Tcl/Tk group at
Sun Microsystems. The rest of the group deserve a lot of credit for turning Tcl and Tk into a dynamite
cross-platform solution. Scott Stanton led the Tk port to the PC. Ray Johnson led the Tk port to the
Macintosh. Jacob Levy implemented the event-driven I/O system, Safe-Tcl, and the browser plug-in.
Brian Lewis built the Tcl compiler. Ken Corey worked on Java integration and helped with the
SpecTcl user interface builder. Syd Polk generalized the menu system to work with native widgets on
the Macintosh and Windows. Colin Stevens generalized the font mechanism and worked on
internationalization for Tk.
Stephen Uhler deserves special thanks for inspiring many of the cool examples I use in this book. He
was the lead on the SpecTcl user interface builder. He built the core HTML display library on which I
based an editor. We worked closely together on the first versions of TclHttpd. He taught me how to
write compact, efficient Tcl code and to use regular expression substitutions in amazing ways. I hope
he has learned at least a little from me.
Thanks again to Mark Taub, Eileen Clark, and Martha Williams at Prentice Hall. George Williams
helped me assemble the files for the CD-ROM.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Third Edition Thanks
John Ousterhout continues his wonderful role as Tcl benefactor, now as founder of Scriptics
Corporation. I'd like to thank every one of the great folks that I work with at Scriptics, especially the
pioneering crew of Sarah Daniels, Scott Stanton, Ray Johnson, Bryan Surles, Melissa Hirschl, Lee
Bernhard, Suresh Sastry, Emil Scaffon, Pat P., Scott Redman, and Berry Kercheval. The rest of the
gang deserves a big thanks for making Scriptics such an enjoyable place to work. Jerry Peek, who is a
notable author himself, provided valuable advice and wonderfully detailed comments! Ken Jones told
me about a great indexing tool.
I'd like to thank all the readers that drop me the encouraging note or probing question via e-mail. I am
always interested in new and interesting uses of Tcl!
Thanks to the editors at Prentice Hall: Mark Taub, Joan McNamara, and Joan Eurell. Mark continues
to encourage me to come out with new editions, and the Joans helped me complete this third edition
on time.
Finally, I thank my wonderful wife Jody for her love, kindness, patience, wit, and understanding as I
worked long hours. Happily, many of those hours were spent working from home. I now have three
sons, Christopher, Daniel, and Michael, who get the credit for keeping me from degenerating into a
complete nerd.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Preface
Contact the Author
I am always open to comments about this book. My e-mail address is [email protected]. It helps me sort
through my mail if you put the word "book" or the title of the book into the e-mail subject line. Visit
my Web site at:
http://www.beedub.com/
for current news about the book and my other interests.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Part I: Tcl Basics
Part I introduces the basics of Tcl. Everyone should read Chapter 1, which describes the
fundamental properties of the language. Tcl is really quite simple, so beginners can pick it up
quickly. The experienced programmer should review Chapter 1 to eliminate any misconceptions
that come from using other languages.
Chapter 2 is a short introduction to running Tcl and Tk on UNIX, Windows, and Macintosh
systems. You may want to look at this chapter first so you can try out the examples as you read
Chapter 1.
Chapter 3 presents a sample application, a CGI script, that implements a guestbook for a Web
site. The example uses several facilities that are described in detail in later chapters. The goal is
to provide a working example that illustrates the power of Tcl.
The rest of Part I covers basic programming with Tcl. Simple string processing is covered in
Chapter 4. Tcl lists, which share the syntax rules of Tcl commands, are explained in Chapter 5.
Control structure like loops and if statements are described in Chapter 6. Chapter 7 describes Tcl
procedures, which are new commands that you write in Tcl. Chapter 8 discusses Tcl arrays.
Arrays are the most flexible and useful data structure in Tcl. Chapter 9 describes file I/O and
running other programs. These facilities let you build Tcl scripts that glue together other
programs and process data in files.
After reading Part I you will know enough Tcl to read and understand other Tcl programs, and to
write simple programs yourself.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 1. Tcl Fundamentals
This chapter describes the basic syntax rules for the Tcl scripting language. It describes the basic
mechanisms used by the Tcl interpreter: substitution and grouping. It touches lightly on the following
Tcl commands: puts, format, set, expr, string, while, incr, and proc.
Tcl is a string-based command language. The language has only a few fundamental constructs and
relatively little syntax, which makes it easy to learn. The Tcl syntax is meant to be simple. Tcl is
designed to be a glue that assembles software building blocks into applications. A simpler glue makes
the job easier. In addition, Tcl is interpreted when the application runs. The interpreter makes it easy to
build and refine your application in an interactive manner. A great way to learn Tcl is to try out
commands interactively. If you are not sure how to run Tcl on your system, see Chapter 2 for
instructions for starting Tcl on UNIX, Windows, and Macintosh systems.
This chapter takes you through the basics of the Tcl language syntax. Even if you are an expert
programmer, it is worth taking the time to read these few pages to make sure you understand the
fundamentals of Tcl. The basic mechanisms are all related to strings and string substitutions, so it is
fairly easy to visualize what is going on in the interpreter. The model is a little different from some
other programming languages with which you may already be familiar, so it is worth making sure you
understand the basic concepts.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Tcl Commands
Tcl stands for Tool Command Language. A command does something for you, like output a string,
compute a math expression, or display a widget on the screen. Tcl casts everything into the mold of a
command, even programming constructs like variable assignment and procedure definition. Tcl adds a
tiny amount of syntax needed to properly invoke commands, and then it leaves all the hard work up to
the command implementation.
The basic syntax for a Tcl command is:
command arg1 arg2 arg3 ...
The command is either the name of a built-in command or a Tcl procedure. White space (i.e., spaces or
tabs) is used to separate the command name and its arguments, and a newline (i.e., the end of line
character) or semicolon is used to terminate a command. Tcl does not interpret the arguments to the
commands except to perform grouping, which allows multiple words in one argument, and
substitution, which is used with programming variables and nested command calls. The behavior of
the Tcl command processor can be summarized in three basic steps:
Argument grouping.
Value substitution of nested commands, variables, and backslash escapes.
Command invocation. It is up to the command to interpret its arguments.
This model is described in detail in this Chapter.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Hello, World!
Example 1-1 The "Hello, World!" example.
puts stdout {Hello, World!}
=> Hello, World!
In this example, the command is puts, which takes two arguments: an I/O stream identifier and a
string. puts writes the string to the I/O stream along with a trailing newline character. There are two
points to emphasize:
Arguments are interpreted by the command. In the example, stdout is used to identify the
standard output stream. The use of stdout as a name is a convention employed by puts and the
other I/O commands. Also, stderr is used to identify the standard error output, and stdin is
used to identify the standard input. Chapter 9 describes how to open other files for I/O.
Curly braces are used to group words together into a single argument. The puts command
receives Hello, World! as its second argument.
The braces are not part of the value.
The braces are syntax for the interpreter, and they get stripped off before the value is passed to the
command. Braces group all characters, including newlines and nested braces, until a matching brace is
found. Tcl also uses double quotes for grouping. Grouping arguments will be described in more detail
later.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Variables
The set command is used to assign a value to a variable. It takes two arguments: The first is the name
of the variable, and the second is the value. Variable names can be any length, and case is significant.
In fact, you can use any character in a variable name.
It is not necessary to declare Tcl variables before you use them.
The interpreter will create the variable when it is first assigned a value. The value of a variable is
obtained later with the dollar-sign syntax, illustrated in Example 1-2:
Example 1-2 Tcl variables.
set var 5
=> 5
set b $var
=> 5
The second set command assigns to variable b the value of variable var. The use of the dollar sign is
our first example of substitution. You can imagine that the second set command gets rewritten by
substituting the value of var for $var to obtain a new command.
set b 5
The actual implementation of substitution is more efficient, which is important when the value is
large.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Command Substitution
The second form of substitution is command substitution. A nested command is delimited by square
brackets, [ ]. The Tcl interpreter takes everything between the brackets and evaluates it as a
command. It rewrites the outer command by replacing the square brackets and everything between
them with the result of the nested command. This is similar to the use of backquotes in other shells,
except that it has the additional advantage of supporting arbitrary nesting of commands.
Example 1-3 Command substitution.
set len [string length foobar]
=> 6
In Example 1-3, the nested command is:
string length foobar
This command returns the length of the string foobar. The string command is described in detail
starting on page 45. The nested command runs first.
Then, command substitution causes the outer command to be rewritten as if it were:
set len 6
If there are several cases of command substitution within a single command, the interpreter processes
them from left to right. As each right bracket is encountered, the command it delimits is evaluated.
This results in a sensible ordering in which nested commands are evaluated first so that their result can
be used in arguments to the outer command.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Math Expressions
The Tcl interpreter itself does not evaluate math expressions. Tcl just does grouping, substitutions and
command invocations. The expr command is used to parse and evaluate math expressions.
Example 1-4 Simple arithmetic.
expr 7.2 / 4
=> 1.8
The math syntax supported by expr is the same as the C expression syntax. The expr command deals
with integer, floating point, and boolean values. Logical operations return either 0 (false) or 1 (true).
Integer values are promoted to floating point values as needed. Octal values are indicated by a leading
zero (e.g., 033 is 27 decimal). Hexadecimal values are indicated by a leading 0x. Scientific notation
for floating point numbers is supported. A summary of the operator precedence is given on page 20.
You can include variable references and nested commands in math expressions. The following
example uses expr to add the value of x to the length of the string foobar. As a result of the innermost
command substitution, the expr command sees 6 + 7, and len gets the value 13:
Example 1-5 Nested commands.
set x 7
set len [expr [string length foobar] + $x]
=> 13
The expression evaluator supports a number of built-in math functions. (For a complete listing, see
page 21.) Example 1-6 computes the value of pi:
Example 1-6 Built-in math functions.
remaining arguments to format are to be formatted. Note that the trailing \n usually found in a C
printf call is not needed because puts provides one for us. For more information about the format
command, see page 52.
Square Brackets Do Not Group
The square bracket syntax used for command substitution does not provide grouping. Instead, a nested
command is considered part of the current group. In the command below, the double quotes group the
last argument, and the nested command is just part of that group.
puts stdout "The length of $s is [string length $s]."
If an argument is made up only of a nested command, you do not need to group it with double-quotes
because the Tcl parser treats the whole nested command as part of the group.
puts stdout [string length $s]
The following is a redundant use of double quotes:
puts stdout "[expr $x + $y]"
Grouping before Substitution
The Tcl parser makes a single pass through a command as it makes grouping decisions and performs
string substitutions. Grouping decisions are made before substitutions are performed, which is an
important property of Tcl. This means that the values being substituted do not affect grouping because
the grouping decisions have already been made.
The following example demonstrates how nested command substitution affects grouping. A nested
command is treated as an unbroken sequence of characters, regardless of its internal structure. It is
included with the surrounding group of characters when collecting arguments for the main command.
Example 1-11 Embedded command and variable substitution.
set x 7; set y 9
puts stdout $x+$y=[expr $x + $y]
=> 7+9=16
In Example 1-11, the second argument to puts is:
$x+$y=[expr $x + $y]
The white space inside the nested command is ignored for the purposes of grouping the argument. By
the time Tcl encounters the left bracket, it has already done some variable substitutions to obtain:
7+9=
When the left bracket is encountered, the interpreter calls itself recursively to evaluate the nested
command. Again, the $x and $y are substituted before calling expr. Finally, the result of expr is
substituted for everything from the left bracket to the right bracket. The puts command gets the
following as its second argument:
7+9=16
Grouping before substitution.
The point of this example is that the grouping decision about puts's second argument is made before
the command substitution is done. Even if the result of the nested command contained spaces or other
special characters, they would be ignored for the purposes of grouping the arguments to the outer
command. Grouping and variable substitution interact the same as grouping and command
substitution. Spaces or special characters in variable values do not affect grouping decisions because
these decisions are made before the variable values are substituted.
If you want the output to look nicer in the example, with spaces around the + and =, then you must use
double quotes to explicitly group the argument to puts:
puts stdout "$x + $y = [expr $x + $y]"
The double quotes are used for grouping in this case to allow the variable and command substitution
on the argument to puts.
Grouping Math Expressions with Braces
It turns out that expr does its own substitutions inside curly braces. This is explained in more detail on
page 15. This means you can write commands like the one below and the substitutions on the variables
in the expression still occur:
puts stdout "$x + $y = [expr {$x + $y}]"
More Substitution Examples
If you have several substitutions with no white space between them, you can avoid grouping with
quotes. The following command sets concat to the value of variables a, b, and c all concatenated
together:
set concat $a$b$c
Again, if you want to add spaces, you'll need to use quotes:
set concat "$a $b $c"
In general, you can place a bracketed command or variable reference anywhere. The following
computes a command name:
[findCommand $x] arg arg
When you use Tk, you often use widget names as command names:
$text insert end "Hello, World!"
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Procedures
Tcl uses the proc command to define procedures. Once defined, a Tcl procedure is used just like any
of the other built-in Tcl commands. The basic syntax to define a procedure is:
proc name arglist body
The first argument is the name of the procedure being defined. The second argument is a list of
parameters to the procedure. The third argument is a command body that is one or more Tcl
commands.
The procedure name is case sensitive, and in fact it can contain any characters. Procedure names and
variable names do not conflict with each other. As a convention, this book begins procedure names
with uppercase letters and it begins variable names with lowercase letters. Good programming style is
important as your Tcl scripts get larger. Tcl coding style is discussed in Chapter 12.
Example 1-12 Defining a procedure.
proc Diag {a b} {
set c [expr sqrt($a * $a + $b * $b)]
return $c
}
puts "The diagonal of a 3, 4 right triangle is [Diag 3 4]"
=> The diagonal of a 3, 4 right triangle is 5.0
The Diag procedure defined in the example computes the length of the diagonal side of a right triangle
given the lengths of the other two sides. The sqrt function is one of many math functions supported
by the expr command. The variable c is local to the procedure; it is defined only during execution of
Diag. Variable scope is discussed further in Chapter 7. It is not really necessary to use the variable c in
this example. The procedure can also be written as:
proc Diag {a b} {
return [expr sqrt($a * $a + $b * $b)]
}
The return command is used to return the result of the procedure. The return command is optional in
this example because the Tcl interpreter returns the value of the last command in the body as the value
of the procedure. So, the procedure could be reduced to:
proc Diag {a b} {
expr sqrt($a * $a + $b * $b)
}
Note the stylized use of curly braces in the example. The curly brace at the end of the first line starts
the third argument to proc, which is the command body. In this case, the Tcl interpreter sees the
opening left brace, causing it to ignore newline characters and scan the text until a matching right
brace is found. Double quotes have the same property. They group characters, including newlines,
until another double quote is found. The result of the grouping is that the third argument to proc is a
sequence of commands. When they are evaluated later, the embedded newlines will terminate each
command.
The other crucial effect of the curly braces around the procedure body is to delay any substitutions in
the body until the time the procedure is called. For example, the variables a, b, and c are not defined
until the procedure is called, so we do not want to do variable substitution at the time Diag is defined.
The proc command supports additional features such as having variable numbers of arguments and
default values for arguments. These are described in detail in Chapter 7.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
A Factorial Example
To reinforce what we have learned so far, below is a longer example that uses a while loop to
compute the factorial function:
Example 1-13 A while loop to compute factorial.
proc Factorial {x} {
set i 1; set product 1
while {$i <= $x} {
set product [expr $product * $i]
incr i
}
return $product
}
Factorial 10
=> 3628800
The semicolon is used on the first line to remind you that it is a command terminator just like the
newline character. The while loop is used to multiply all the numbers from one up to the value of x.
The first argument to while is a boolean expression, and its second argument is a command body to
execute. The while/ command and other control structures are described in Chapter 6.
The same math expression evaluator used by the expr command is used by while to evaluate the
boolean expression. There is no need to explicitly use the expr command in the first argument to
while, even if you have a much more complex expression.
The loop body and the procedure body are grouped with curly braces in the same way. The opening
curly brace must be on the same line as proc and while. If you like to put opening curly braces on the
line after a while or if statement,
you must escape the newline with a backslash:
The Tcl interpreter makes some assumptions about variable names that make it easy to embed variable
references into other strings. By default, it assumes that variable names contain only letters, digits, and
the underscore. The construct $foo.o represents a concatenation of the value of foo and the literal
".o".
If the variable reference is not delimited by punctuation or white space, then you can use curly braces
to explicitly delimit the variable name (e.g., ${x}). You can also use this to reference variables with
funny characters in their name, although you probably do not want variables named like that. If you
find yourself using funny variable names, or computing the names of variables, then you may want to
use the upvar command.
Example 1-16 Embedded variable references.
set foo filename
set object $foo.o
=> filename.o
set a AAA
set b abc${a}def
=> abcAAAdef
set .o yuk!
set x ${.o}y
=> yuk!y
The unset Command
You can delete a variable with the unset command:
unset varName varName2 ...
Any number of variable names can be passed to the unset command. However, unset will raise an
error if a variable is not already defined.
Using info to Find Out about Variables
The existence of a variable can be tested with the info exists command. For example, because incr
requires that a variable exist, you might have to test for the existence of the variable first.
Example 1-17 Using info to determine if a variable exists.
if {![info exists foobar]} {
set foobar 0
} else {
incr foobar
}
Example 7-6 on page 86 implements a new version of incr which handles this case.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
More about Math Expressions
This section describes a few fine points about math in Tcl scripts. In Tcl 7.6 and earlier versions math
is not that efficient because of conversions between strings and numbers. The expr command must
convert its arguments from strings to numbers. It then does all its computations with double precision
floating point values. The result is formatted into a string that has, by default, 12 significant digits.
This number can be changed by setting the tcl_precision variable to the number of significant digits
desired. Seventeen digits of precision are enough to ensure that no information is lost when converting
back and forth between a string and an IEEE double precision number:
Example 1-18 Controlling precision with tcl_precision.
expr 1 / 3
=> 0
expr 1 / 3.0
=> 0.333333333333
set tcl_precision 17
=> 17
expr 1 / 3.0
# The trailing 1 is the IEEE rounding digit
=> 0.33333333333333331
In Tcl 8.0 and later versions, the overhead of conversions is eliminated in most cases by the built-in
compiler. Even so, Tcl was not designed to support math-intensive applications. You may want to
implement math-intensive code in a compiled language and register the function as a Tcl command as
described in Chapter 44.
There is support for string comparisons by expr, so you can test string values in if statements. You
must use quotes so that expr knows to do string comparisons:
if {$answer == "yes"} {... }
However, the string compare and string equal commands described in Chapter 4 are more
reliable because expr may do conversions on strings that look like numbers. The issues with string
operations and expr are discussed on page 48.
Expressions can include variable and command substitutions and still be grouped with curly braces.
This is because an argument to expr is subject to two rounds of substitution: one by the Tcl interpreter,
and a second by expr itself. Ordinarily this is not a problem because math values do not contain the
characters that are special to the Tcl interpreter. The second round of substitutions is needed to support
commands like while and if that use the expression evaluator internally.
Grouping expressions can make them run more efficiently.
You should always group expressions in curly braces and let expr do command and variable
substitutions. Otherwise, your values may suffer extra conversions from numbers to strings and back
to numbers. Not only is this process slow, but the conversions can loose precision in certain
circumstances. For example, suppose x is computed from a math function:
set x [expr {sqrt(2.0)}]
At this point the value of x is a double-precision floating point value, just as you would expect. If you
do this:
set two [expr $x * $x]
then you may or may not get 2.0 as the result! This is because Tcl will substitute $x and expr will
concatenate all its arguments into one string, and then parse the expression again. In contrast, if you do
this:
set two [expr {$x * $x}]
then expr will do the substitutions, and it will be careful to preserve the floating point value of x. The
expression will be more accurate and run more efficiently because no string conversions will be done.
The story behind Tcl values is described in more detail in Chapter 44 on C programming and Tcl.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Comments
Tcl uses the pound character, #, for comments. Unlike in many other languages, the # must occur at
the beginning of a command. A # that occurs elsewhere is not treated specially. An easy trick to
append a comment to the end of a command is to precede the # with a semicolon to terminate the
previous command:
# Here are some parameters
set rate 7.0 ;# The interest rate
set months 60 ;# The loan term
One subtle effect to watch for is that a backslash effectively continues a comment line onto the next
line of the script. In addition, a semicolon inside a comment is not significant. Only a newline
terminates comments:
# Here is the start of a Tcl comment \
and some more of it; still in the comment
The behavior of a backslash in comments is pretty obscure, but it can be exploited as shown in
Example 2-3 on page 27.
A surprising property of Tcl comments is that curly braces inside comments are still counted for the
purposes of finding matching brackets. I think the motivation for this mis-feature was to keep the
original Tcl parser simpler. However, it means that the following will not work as expected to
comment out an alternate version of an if expression:
# if {boolean expression1} {
if {boolean expression2} {
some commands
}
The previous sequence results in an extra left curly brace, and probably a complaint about a missing
close brace at the end of your script! A technique I use to comment out large chunks of code is to put
the code inside an if block that will never execute:
if {0} {
unused code here
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Substitution and Grouping Summary
The following rules summarize the fundamental mechanisms of grouping and substitution that are
performed by the Tcl interpreter before it invokes a command:
Command arguments are separated by white space, unless arguments are grouped with curly
braces or double quotes as described below.
Grouping with curly braces, { }, prevents substitutions. Braces nest. The interpreter includes all
characters between the matching left and right brace in the group, including newlines,
semicolons, and nested braces. The enclosing (i.e., outermost) braces are not included in the
group's value.
Grouping with double quotes, " ", allows substitutions. The interpreter groups everything until
another double quote is found, including newlines and semicolons. The enclosing quotes are not
included in the group of characters. A double-quote character can be included in the group by
quoting it with a backslash, (e.g., \").
Grouping decisions are made before substitutions are performed, which means that the values of
variables or command results do not affect grouping.
A dollar sign, $, causes variable substitution. Variable names can be any length, and case is
significant. If variable references are embedded into other strings, or if they include characters
other than letters, digits, and the underscore, they can be distinguished with the ${varname}
syntax.
Square brackets, [ ], cause command substitution. Everything between the brackets is treated as
a command, and everything including the brackets is replaced with the result of the command.
Nesting is allowed.
The backslash character, \, is used to quote special characters. You can think of this as another
form of substitution in which the backslash and the next character or group of characters are
replaced with a new character.
Substitutions can occur anywhere unless prevented by curly brace grouping. Part of a group can
be a constant string, and other parts of it can be the result of substitutions. Even the command
name can be affected by substitutions.
the current group. The following sets x to the concatenation of two command results because
there is no space between ] and [.
set x [cmd1][cmd2]
Newlines and semicolons are ignored when grouping with braces or double quotes. They get
included in the group of characters just like all the others. The following sets x to a string that
contains newlines:
set x "This is line one.
This is line two.
This is line three."
During command substitution, newlines and semicolons are significant as command terminators.
If you have a long command that is nested in square brackets, put a backslash before the newline
if you want to continue the command on another line. This was illustrated in Example 1-9 on
page 8.
A dollar sign followed by something other than a letter, digit, underscore, or left parenthesis is
treated as a literal dollar sign. The following sets x to the single character $.
set x $
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter1. Tcl Fundamentals
Reference
Backslash Sequences
Table 1-1. Backslash sequences.
\a
Bell. (0x7)
\b
Backspace. (0x8)
\f
Form feed. (0xc)
\n
Newline. (0xa)
\r
Carriage return. (0xd)
\t
Tab. (0x9)
\v
Vertical tab. (0xb)
\<newline>
Replace the newline and the leading white space on the next line with a space.
\\
Backslash. ('\')
\ooo Octal specification of character code. 1, 2, or 3 digits.
\xhh Hexadecimal specification of character code. 1 or 2 digits.
\uhhhh Hexadecimal specification of a 16-bit Unicode character value. 4 hex digits.
\c Replaced with literal c if c is not one of the cases listed above. In particular, \$, \", \{,
\}, \], and \[ are used to obtain these characters.
Arithmetic Operators
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter2. Getting Started
The source Command
You can enter Tcl commands interactively at the % prompt. It is a good idea to try out the examples in
this book as you read along. The highlighted examples from the book are on the CD-ROM in the
exsource folder. You can edit these scripts in your favorite editor. Save your examples to a file and
then execute them with the Tcl source command:
source filename
The source command reads Tcl commands from a file and evaluates them just as if you had typed
them interactively.
Chapter 3 develops a sample application. To get started, just open an editor on a file named cgi1.tcl.
Each time you update this file you can save it, reload it into Tcl with the source command, and test it
again. Development goes quickly because you do not wait for things to compile!
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter2. Getting Started
UNIX Tcl Scripts
On UNIX you can create a stand alone Tcl or Tcl/Tk script much like an sh or csh script. The trick is
in the first line of the file that contains your script. If the first line of a file begins with #!pathname,
then UNIX uses pathname as the interpreter for the rest of the script. The "Hello, World!" program
from Chapter 1 is repeated in Example 2-1 with the special starting line:
Example 2-1 A standalone Tcl script on UNIX.
#!/usr/local/bin/tclsh
puts stdout {Hello, World!}
Similarly, the Tk hello world program from Chapter 21 is shown in Example 2-2:
Example 2-2 A standalone Tk script on UNIX.
#!/usr/local/bin/wish
button .hello -text Hello -command {puts "Hello, World!"}
pack .hello -padx 10 -pady 10
The actual pathnames for tclsh and wish may be different on your system. If you type the pathname for
the interpreter wrong, you receive a confusing "command not found" error. You can find out the
complete pathname of the Tcl interpreter with the info nameofexecutable command. This is what
appears on my system:
info nameofexecutable
=> /home/welch/install/solaris/bin/tclsh8.2
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter2. Getting Started
The Macintosh and ResEdit
If you want to create a self-contained Tcl/Tk application on Macintosh, you must copy the Wish
program and add a Macintosh resource named tclshrc that has the start-up Tcl code. The Tcl code
can be a single source command that reads your script file. Here are step-by-step instructions to create
the resource using ResEdit:
First, make a copy of Wish and open the copy in ResEdit.
Pull down the Resource menu and select Create New Resource operation to make a new TEXT
resource.
ResEdit opens a window and you can type in text. Type in a source command that names your
script:
source "Hard Disk:Tcl/Tk 8.1:Applications:MyScript.tcl"
Set the name of the resource to be tclshrc. You do this through the Get Resource Info dialog
under the Resources menu in ResEdit.
This sequence of commands is captured in an application called "Drag n Drop Tclets", which comes
with the Macintosh Tcl distribution. If you drag a Tcl script onto this icon, it will create a copy of Wish
and create the tclshrc text resource that has a source command that will load that script.
If you have a Macintosh development environment, you can build a version of Wish that has additional
resources built right in. You add the resources to the applicationInit.r file. If a resource contains
Tcl code, you use it like this:
source -rcrc resource
If you don't want to edit resources, you can just use the Wish Source menu to select a script to run.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter2. Getting Started
The console Command
The Windows and Macintosh platforms have a built-in console that is used to enter Tcl commands
interactively. You can control this console with the console command. The console is visible by
default. Hide the console like this:
console hide
Display the console like this:
console show
The console is implemented by a second Tcl interpreter. You can evaluate Tcl commands in that
interpreter with:
console eval command
There is an alternate version of this console called TkCon. It is included on the CD-ROM, and you can
find current versions on the Internet. TkCon was created by Jeff Hobbs and has lots of nice features.
You can use TkCon on Unix systems, too.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter2. Getting Started
Command-Line Arguments
If you run a script from the command line, for example from a UNIX shell, you can pass the script
command-line arguments. You can also specify these arguments in the shortcut command in
Windows. For example, under UNIX you can type this at a shell:
% myscript.tcl arg1 arg2 arg3
In Windows, you can have a shortcut that runs wish on your script and also passes additional
arguments:
"c:\Program Files\TCL82\wish.exe" c:\your\script.tcl arg1
The Tcl shells pass the command-line arguments to the script as the value of the argv variable. The
number of command-line arguments is given by the argc variable. The name of the program, or script,
is not part of argv nor is it counted by argc. Instead, it is put into the argv0 variable. Table 2-2 lists all
the predefined variables in the Tcl shells. argv is a list, so you can use the lindex command, which is
described on page 59, to extract items from it:
set arg1 [lindex $argv 0]
The following script prints its arguments (foreach is described on page 73):
Example 2-4 The EchoArgs script.
# Tcl script to echo command line arguments
puts "Program: $argv0"
puts "Number of arguments: $argc"
set i 0
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 3. The Guestbook CGI Application
This chapter presents a simple Tcl program that computes a Web page. The chapter provides a brief
background to HTML and the CGI interface to Web servers.
This chapter presents a complete, but simple guestbook program that computes an HTML document,
or Web page, based on the contents of a simple database. The basic idea is that a user with a Web
browser visits a page that is computed by the program. The details of how the page gets from your
program to the user with the Web browser vary from system to system. The Tcl Web Server described
in Chapter 18 comes with this guestbook example already set up. You can also use these scripts on
your own Web server, but you will need help from your Webmaster to set things up.
The chapter provides a very brief introduction to HTML and CGI programming. HTML is a way to
specify text formatting, including hypertext links to other pages on the World Wide Web. CGI is a
standard for communication between a Web server that delivers documents and a program that
computes documents for the server. There are many books on these subjects alone. CGI Developers
Resource, Web Programming with Tcl and Perl by John Ivler (Prentice Hall, 1997) is a good reference
for details that are left unexplained here.
A guestbook is a place for visitors to sign their name and perhaps provide other information. We will
build a guestbook that takes advantage of the World Wide Web. Our guests can leave their address as
a Universal Resource Location (URL). The guestbook will be presented as a page that has hypertext
links to all these URLs so that other guests can visit them. The program works by keeping a simple
database of the guests, and it generates the guestbook page from the database.
The Tcl scripts described in this chapter use commands and techniques that are described in more
detail in later chapters. The goal of the examples is to demonstrate the power of Tcl without
explaining every detail. If the examples in this chapter raise questions, you can follow the references to
examples in other chapters that do go into more depth.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter3. The Guestbook CGI Application
A Quick Introduction to HTML
Web pages are written in a text markup language called HTML (HyperText Markup Language). The
idea of HTML is that you annotate, or mark up, regular text with special tags that indicate structure
and formatting. For example, the title of a Web page is defined like this:
<TITLE>My Home Page</TITLE>
The tags provide general formatting guidelines, but the browsers that display HTML pages have
freedom in how they display things. This keeps the markup simple. The general syntax for HTML tags
is:
<tag parameters>normal text</tag>
As shown here, the tags usually come in pairs. The open tag may have some parameters, and the close
tag name begins with a slash. The case of a tag is not considered, so <title>, <Title>, and <TITLE>
are all valid and mean the same thing. The corresponding close tag could be </title>, </Title>,
</TITLE>, or even </TiTlE>.
The <A> tag defines hypertext links that reference other pages on the Web. The hypertext links connect
pages into a Web so that you can move from page to page to page and find related information. It is
the flexibility of the links that make the Web so interesting. The <A> tag takes an HREF parameter that
defines the destination of the link. If you wanted to link to my home page, you would put this in your
page:
<A HREF="http://www.beedub.com/">Brent Welch</A>
When this construct appears in a Web page, your browser typically displays "Brent Welch" in blue
underlined text. When you click on that text, your browser switches to the page at the address
"http://www.beedub.com/". There is a lot more to HTML, of course, but this should give you a basic
idea of what is going on in the examples. The following list summarizes the HTML tags that will be
The clock command is used twice: once to get the current time in seconds, and a second time to
format the time into a nice looking string. The clock command is described in detail on page 173.
Fortunately, there is no conflict between the markup syntax used by HTML and the Tcl syntax for
embedded commands, so we can mix the two in the argument to the puts command. Double quotes
are used to group the argument to puts so that the clock commands will be executed. When run, the
output of the program will look like this:
Example 3-2 Output of Example 3-1.
Content-Type: text/html
<TITLE>The Current Time</TITLE>
The time is <B>Wed Oct 16 11:23:43 1996</B>
This example is a bit sloppy in its use of HTML, but it should display properly in most Web browsers.
Example 3-3 includes all the required tags for a proper HTML document.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter3. The Guestbook CGI Application
The guestbook.cgi Script
The guestbook.cgi script computes a page that lists all the registered guests. The example is shown
first, and then each part of it is discussed in more detail later. One thing to note right away is that the
HTML tags are generated by procedures that hide the details of the HTML syntax. The first lines of
the script use the UNIX trick to have tclsh interpret the script. This trick is described on page 26:
Example 3-3 The guestbook.cgi script.
#!/bin/sh
# guestbook.cgi
# Implement a simple guestbook page.
# The set of visitors is kept in a simple database.
# The newguest.cgi script will update the database.
# \
exec tclsh "$0" ${1+"$@"}
# The cgilib.tcl file has helper procedures
# The guestbook.data file has the database
# Both file are in the same directory as the script
set dir [file dirname [info script]]
source [file join $dir cgilib.tcl]
set datafile [file join $dir guestbook.data]
Cgi_Header "Brent's Guestbook" {BGCOLOR=white TEXT=black}
P
if {![file exists $datafile]} {
puts "No registered guests, yet."
P
puts "Be the first [Link {registered guest!}newguest.html]"
} else {
puts "The following folks have registered in my GuestBook."
P
puts [Link Register newguest.html]
H2 Guests
catch {source $datafile}
foreach name [lsort [array names Guestbook]] {
set item $Guestbook($name)
set homepage [lindex $item 0]
set markup [lindex $item 1]
H3 [Link $name $homepage]
puts $markup
}
}
Cgi_End
Using a Script Library File
The script uses a number of Tcl procedures that make working with HTML and the CGI interface
easier. These procedures are kept in the cgilib.tcl file, which is kept in the same directory as the
main script. The script starts by sourcing the cgilib.tcl file so that these procedures are available.
The following command determines the location of the cgilib.tcl file based on the location of the
main script. The info script command returns the file name of the script. The file dirname and
file join commands manipulate file names in a platform-independent way. They are described on
page 102. I use this trick to avoid putting absolute file names into my scripts, which would have to be
changed if the program moves later:
set dir [file dirname [info script]]
source [file join $dir cgilib.tcl]
Beginning the HTML Page
The following command generates the standard information that comes at the beginning of an HTML
page:
Cgi_Header {Brent's GuestBook} {bgcolor=white text=black}
The Cgi_Header is shown in Example 3-4:
Example 3-4 The Cgi_Header procedure.
proc Cgi_Header {title {bodyparams {}}} {
puts stdout \
"Content-Type: text/html
<HTML>
<HEAD>
<TITLE>$title</TITLE>
</HEAD>
<BODY $bodyparams>
<H1>$title</H1>"
}
The Cgi_Header procedure takes as arguments the title for the page and some optional parameters for
the HTML <Body> tag. The guestbook.cgi script specifies black text on a white background to avoid
the standard gray background of most browsers. The procedure definition uses the syntax for an
optional parameter, so you do not have to pass bodyparams to Cgi_Header. Default values for
procedure parameters are described on page 81.
The Cgi_Header procedure just contains a single puts command that generates the standard
boilerplate that appears at the beginning of the output. Note that several lines are grouped together
with double quotes. Double quotes are used so that the variable references mixed into the HTML are
substituted properly.
The output begins with the CGI content-type information, a blank line, and then the HTML. The
HTML is divided into a head and a body part. The <TITLE> tag goes in the head section of an HTML
document. Finally, browsers display the title in a different place than the rest of the page, so I always
want to repeat the title as a level-one heading (i.e., H1) in the body of the page.
Simple Tags and Hypertext Links
The next thing the program does is to see whether there are any registered guests or not. The file
command, which is described in detail on page 102, is used to see whether there is any data:
if {![file exists $datafile]} {
If the database file does not exist, a different page is displayed to encourage a registration. The page
includes a hypertext link to a registration page. The newguest.html page will be described in more
detail later:
puts "No registered guests, yet."
P
puts "Be the first [Link {registered guest!}newguest.html]"
The P command generates the HTML for a paragraph break. This trivial procedure saves us a few
keystrokes:
proc P {} {
puts <P>
}
The Link command formats and returns the HTML for a hypertext link. Instead of printing the HTML
directly, it is returned, so you can include it in-line with other text you are printing:
Example 3-5 The Link command formats a hypertext link.
proc Link {text url} {
return "<A HREF=\"$url\">$text</A>"
}
The output of the program would be as below if there were no data:
Example 3-6 Initial output of guestbook.cgi.
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>Brent's Guestbook</TITLE>
</HEAD>
<BODY BGCOLOR=white TEXT=black>
<H1>Brent's Guestbook</H1>
<P>
No registered guests.
<P>
Be the first <A HREF="newguest.html">registered guest!</A>
</BODY>
</HTML>
If the database file exists, then the real work begins. We first generate a link to the registration page,
and a level-two header to separate that from the guest list:
puts [Link Register newguest.html]
H2 Guests
The H2 procedure handles the detail of including the matching close tag:
proc H2 {string} {
puts "<H2>$string</H2>"
}
Using a Tcl Array for the Database
The datafile contains Tcl commands that define an array that holds the guestbook data. If this file is
kept in the same directory as the guestbook.cgi script, then you can compute its name:
set dir [file dirname [info script]]
set datafile [file join $dir guestbook.data]
By using Tcl commands to represent the data, we can load the data with the source command. The
catch command is used to protect the script from a bad data file, which will show up as an error from
the source command. Catching errors is described in detail on page 79:
catch {source $datafile}
The Guestbook variable is the array defined in guestbook.data. Array variables are the topic of
Chapter 8. Each element of the array is defined with a Tcl command that looks like this:
set Guestbook(key) {url markup}
The person's name is the array index, or key. The value of the array element is a Tcl list with two
elements: their URL and some additional HTML markup that they can include in the guestbook. Tcl
lists are the topic of Chapter 5. The following example shows what the command looks like with real
data:
set {Guestbook(Brent Welch)} {
http://www.beedub.com/
{<img src=http://www.beedub.com/welch.gif>}
}
The spaces in the name result in additional braces to group the whole variable name and each list
element. This syntax is explained on page 90. Do not worry about it now. We will see on page 42 that
all the braces in the previous statement are generated automatically. The main point is that the person's
name is the key, and the value is a list with two elements.
The array names command returns all the indices, or keys, in the array, and the lsort command sorts
these alphabetically. The foreach command loops over the sorted list, setting the loop variable x to
each key in turn:
foreach name [lsort [array names Guestbook]] {
Given the key, we get the value like this:
set item $Guestbook($name)
The two list elements are extracted with lindex, which is described on page 63.
set homepage [lindex $item 0]
set markup [lindex $item 1]
We generate the HTML for the guestbook entry as a level-three header that contains a hypertext link to
the guest's home page. We follow the link with any HTML markup text that the guest has supplied to
embellish his or her entry. The H3 procedure is similar to the H2 procedure already shown, except it
generates <H3> tags:
H3 [Link $name $homepage]
puts $markup
Sample Output
The last thing the script does is call Cgi_End to output the proper closing tags. Example 3-7 shows the
output of the guestbook.cgi script:
Example 3-7 Output of guestbook.cgi.
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>Brent's Guestbook</TITLE>
</HEAD>
<BODY BGCOLOR=white TEXT=black>
<H1>Brent's Guestbook</H1>
<P>
The following folks have registered in my guestbook.
<P>
<A HREF="newguest.html">Register</A>
<H2>Guests</H2>
<H3><A HREF="http://www.beedub.com/">Brent Welch</A></H3>
<IMG SRC="http://www.beedub.com/welch.gif">
</BODY>
</HTML>
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter3. The Guestbook CGI Application
Defining Forms and Processing Form Data
The guestbook.cgi script only generates output. The other half of CGI deals with input from the user. Input
is more complex for two reasons. First, we have to define another HTML page that has a form for the user to
fill out. Second, the data from the form is organized and encoded in a standard form that must be decoded by
the script. Example 3-8 on page 40 defines a very simple form, and the procedure that decodes the form data
is shown in Example 11-6 on page 155.
The guestbook page contains a link to newguest.html . This page contains a form that lets a user register his
or her name, home page URL, and some additional HTML markup. The form has a submit button. When a
user clicks that button in their browser, the information from the form is passed to the newguest.cgi script.
This script updates the database and computes another page for the user that acknowledges the user's
contribution.
The newguest.html Form
An HTML form contains tags that define data entry fields, buttons, checkboxes, and other elements that let
the user specify values. For example, a one-line entry field that is used to enter the home page URL is defined
like this:
<INPUT TYPE=text NAME=url>
The INPUT tag is used to define several kinds of input elements, and its type parameter indicates what kind.
In this case, TYPE=text creates a one-line text entry field. The submit button is defined with an INPUT tag that
has TYPE=submit , and the VALUE parameter becomes the text that appears on the button:
<INPUT TYPE=submit NAME=submit VALUE=Register>
A general type-in window is defined with the TEXTAREA tag. This creates a multiline, scrolling text field that
is useful for specifying lots of information, such as a free-form comment. In our case we will let guests type
in HTML that will appear with their guestbook entry. The text between the open and close TEXTAREA tags is
inserted into the type-in window when the page is first displayed.
<TEXTAREA NAME=markup ROWS=10 COLS=50>Hello.</TEXTAREA>
A common parameter to the form tags is NAME= something . This name identifies the data that will come back
from the form. The tags also have parameters that affect their display, such as the label on the submit button
and the size of the text area. Those details are not important for our example. The complete form is shown in
Example 3-8 :
Example 3-8 The newguest.html form.
<!Doctype HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
<HEAD>
<TITLE>Register in my Guestbook</TITLE>
<!-- Author: bwelch -->
<META HTTP-Equiv=Editor Content="SunLabs WebTk 1.0beta 10/11/96">
</HEAD>
<BODY>
<FORM ACTION="newguest.cgi" METHOD="POST">
<H1>Register in my Guestbook</H1>
<UL>
<LI>Name <INPUT TYPE="text" NAME="name" SIZE="40">
<LI>URL <INPUT TYPE="text" NAME="url" SIZE="40">
<P>
If you don't have a home page, you can use an email URL like "mailto:[email protected]"
<LI>Additional HTML to include after your link:
<BR>
<TEXTAREA NAME="html" COLS="60" ROWS="15">
</TEXTAREA>
<LI><INPUT TYPE="submit" NAME="new" VALUE="Add me to your guestbook">
<LI><INPUT TYPE="submit" NAME="update" VALUE="Update my guestbook entry">
</UL>
</FORM>
</BODY>
</HTML>
The newguest.cgi Script
When the user clicks the Submit button in their browser, the data from the form is passed to the program
identified by the Action parameter of the form tag. That program takes the data, does something useful with
it, and then returns a new page for the browser to display. In our case the FORM tag names newguest.cgi as
the program to handle the data:
<FORM ACTION=newguest.cgi METHOD=POST>
The CGI specification defines how the data from the form is passed to the program. The data is encoded and
organized so that the program can figure out the values the user specified for each form element. The
encoding is handled rather nicely with some regular expression tricks that are done in Cgi_Parse . Cgi_Parse
saves the form data, and Cgi_Value gets a form value in the script. These procedures are described in
Example 11-6 on page 155. Example 3-9 starts out by calling Cgi_Parse :
Example 3-9 The newguest.cgi script.
#!/bin/sh
# \
exec tclsh "$0" ${1+"$@"}
# source cgilib.tcl from the same directory as newguest.cgi
set dir [file dirname [info script]]
source [file join $dir cgilib.tcl]
set datafile [file join $dir guestbook.data]
Cgi_Parse
# Open the datafile in append mode
if [catch {open $datafile a}out] {
Cgi_Header "Guestbook Registration Error" \
{BGCOLOR=black TEXT=red}
P
puts "Cannot open the data file"
P
puts $out;# the error message
exit 0
}
# Append a Tcl set command that defines the guest's entry
puts $out ""
puts $out [list set Guestbook([Cgi_Value name]) \
[list [Cgi_Value url] [Cgi_Value html]]]
close $out
# Return a page to the browser
Cgi_Header "Guestbook Registration Confirmed" \
{BGCOLOR=white TEXT=black}
puts "
<DL>
<DT>Name
<DD>[Cgi_Value name]
<DT>URL
<DD>[Link [Cgi_Value url] [Cgi_Value url]]
</DL>
[Cgi_Value html]
"
Cgi_End
The main idea of the newguest.cgi script is that it saves the data to a file as a Tcl command that defines an
element of the Guestbook array. This lets the guestbook.cgi script simply load the data by using the Tcl
source command. This trick of storing data as a Tcl script saves us from the chore of defining a new file
format and writing code to parse it. Instead, we can rely on the well-tuned Tcl implementation to do the hard
work for us efficiently.
The script opens the datafile in append mode so that it can add a new record to the end. Opening files is
described in detail on page 110. The script uses a catch command to guard against errors. If an error occurs,
a page explaining the error is returned to the user. Working with files is one of the most common sources of
errors (permission denied, disk full, file-not-found, and so on), so I always open the file inside a catch
statement:
if [catch {open $datafile a} out] {
# an error occurred
} else {
# open was ok
}
In this command, the variable out gets the result of the open command, which is either a file descriptor or an
error message. This style of using catch is described in detail in Example 6-14 on page 77.
The script writes the data as a Tcl set command. The list command is used to format the data properly:
puts $out [list set Guestbook([Cgi_Value name]) \
[list [Cgi_Value url] [Cgi_Value html]]]
There are two lists. First the url and html values are formatted into one list. This list will be the value of the
array element. Then, the whole Tcl command is formed as a list. In simplified form, the command is
generated from this:
list set variable value
Using the list command ensures that the result will always be a valid Tcl command that sets the variable to
the given value. The list command is described in more detail on page 61.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter3. The Guestbook CGI Application
The cgi.tcl Package
The cgilib.tcl file included with this book just barely scratches the surface of things you might like
to do in a CGI script. Don Libes has created a comprehensive package for CGI scripts known as
cgi.tcl. You can find it on the web at
http://expect.nist.gov/cgi.tcl/
One of Don's goals in cgi.tcl was to eliminate the need to directly write any HTML markup at all.
Instead, he has defined a whole suite of Tcl commands similar to the P and H2 procedures shown in
this chapter that automatically emit the matching close tags. He also has support procedures to deal
with browser cookies, page redirects, and other CGI features.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter3. The Guestbook CGI Application
Next Steps
There are a number of details that can be added to this example. A user may want to update their entry,
for example. They could do that now, but they would have to retype everything. They might also like a
chance to check the results of their registration and make changes before committing them. This
requires another page that displays their guest entry as it would appear on a page, and also has the
fields that let them update the data.
The details of how a CGI script is hooked up with a Web server vary from server to server. You
should ask your local Webmaster for help if you want to try this out on your local web site. The Tcl
Web Server comes with this guestbook example already set up, plus it has a number of other very
interesting ways to generate pages. My own taste in web page generation has shifted from CGI to a
template-based approach supported by the Tcl Web Server. This is the topic of Chapter 18.
The next few chapters describe basic Tcl commands and data structures. We return to this example in
Chapter 11 on regular expressions.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 4. String Processing in Tcl
This chapter describes string manipulation and simple pattern matching. Tcl commands described are:
string, append, format, scan, and binary. The string command is a collection of several useful
string manipulation operations.
Strings are the basic data item in Tcl, so it should not be surprising that there are a large number of
commands to manipulate strings. A closely related topic is pattern matching, in which string
comparisons are made more powerful by matching a string against a pattern. This chapter describes a
simple pattern matching mechanism that is similar to that used in many other shell languages. Chapter
11 describes a more complex and powerful regular expression pattern matching mechanism.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter4. String Processing in Tcl
The string Command
The string command is really a collection of operations you can perform on strings. The following
example calculates the length of the value of a variable.
set name "Brent Welch"
string length $name
=> 11
The first argument to string determines the operation. You can ask string for valid operations by
giving it a bad one:
string junk
=> bad option "junk": should be bytelength, compare,
equal, first, index, is, last, length, map, match, range,
repeat, replace, tolower, totitle, toupper, trim, trimleft,
trimright, wordend, or wordstart
This trick of feeding a Tcl command bad arguments to find out its usage is common across many
commands. Table 4-1 summarizes the string command
Table 4-1. The string command.
string bytelength
str
Returns the number of bytes used to store a string, which may be different
from the character length returned by string length because of UTF-8
encoding. See page 210 of Chapter 15 about Unicode and UTF-8.
string compare ?-
nocase? ?-length
len? str1 str2
Compares strings lexicographically. Use -nocase for case insensitve
comparison. Use -length to limit the comparison to the first len characters.
Returns 0 if equal, -1 if str1 sorts before str2, else 1.
string equal ?-
nocase? str1 str2
Compares strings and returns 1 if they are the same. Use -nocase for case
insensitve comparison.
string first str1
str2
Returns the index in str2 of the first occurrence of str1, or -1 if str1 is not
found.
string index string
index
Returns the character at the specified index. An index counts from zero. Use
end for the last character.
string is class ?-
strict? ?-
failindex varname?
string
Returns 1 if string belongs to class. If -strict, then empty strings never
match, otherwise they always match. If -failindex is specified, then
varname is assigned the index of the character in string that prevented it
from being a member of class. See Table 4-3 on page 50 for character class
names.
string last str1
str2
Returns the index in str2 of the last occurrence of str1, or -1 if str1 is not
found.
string length
string
Returns the number of characters in string.
string map ?-
nocase? charMap
string
Returns a new string created by mapping characters in string according to
the input, output list in charMap. See page 51.
string match
pattern str
Returns 1 if str matches the pattern, else 0. Glob-style matching is used.
See page 48.
string range str i
j
Returns the range of characters in str from i to j.
string repeat str
count
Returns str repeated count times.
string replace str
first last?newstr?
Returns a new string created by replacing characters first through last
with newstr, or nothing.
string tolower
string?first? ?
last?
Returns string in lower case. first and last determine the range of
string on which to operate.
string totitle
string?first? ?
last?
Capitalizes string by replacing its first character with the Unicode title
case, or upper case, and the rest with lower case. first and last determine
the range of string on which to operate.
string toupper
string?first? ?
last?
Returns string in upper case. first and last determine the range of
string on which to operate.
string trim
string?chars?
Trims the characters in chars from both ends of string. chars defaults to
whitespace.
string trimleft
string?chars?
Trims the characters in chars from the beginning of string. chars defaults
to whitespace.
string trimright
string?chars?
Trims the characters in chars from the end of string. chars defaults to
whitespace.
string wordend str
ix
Returns the index in str of the character after the word containing the
character at index ix.
string wordstart
str ix
Returns the index in str of the first character in the word containing the
character at index ix.
These are the string operations I use most:
The equal operation, which is shown in Example 4-2 on page 48.
String match. This pattern matching operation is described on page 48.
The tolower, totitle, and toupper operations convert case.
The trim, trimright, and trimleft operations are handy for cleaning up strings.
These new operations were added in Tcl 8.1 (actually, they first appeared in the 8.1.1 patch release):
The equal operation, which is simpler than using string compare.
The is operation that test for kinds of strings. String classes are listed in Table 4-3 on page 50.
The map operation that translates characters (e.g., like the Unix tr command.)
The repeat and replace operations.
The totitle operation, which is handy for capitalizing words.
String Indices
Several of the string operations involve string indices that are positions within a string. Tcl counts
characters in strings starting with zero. The special index end is used to specify the last character in a
string:
string range abcd 2 end
=> cd
Tcl 8.1 added syntax for specifying an index relative to the end. Specify end-N to get the Nth caracter
before the end. For example, the following command returns a new string that drops the first and last
characters from the original:
string range $string 1 end-1
There are several operations that pick apart strings: first, last, wordstart, wordend, index, and
range. If you find yourself using combinations of these operations to pick apart data, it will be faster if
you can do it with the regular expression pattern matcher described in Chapter 11.
Strings and Expressions
Strings can be compared with expr, if, and while using the comparison operators ==, !=, < and >.
However, there are a number of subtle issues that can cause problems. First, you must quote the string
value so that the expression parser can identify it as a string type. Then, you must group the expression
with curly braces to prevent the double quotes from being stripped off by the main interpreter:
if {$x == "foo"}command
expr is unreliable for string comparison.
Ironically, despite the quotes, the expression evaluator first converts items to numbers if possible, and
then converts them back if it detects a case of string comparison. The conversion back is always done
as a decimal number. This can lead to unexpected conversions between strings that look like
hexadecimal or octal numbers. The following boolean expression is true!
if {"0xa" == "10"} {puts stdout ack! }
=> ack!
A safe way to compare strings is to use the string compare and equal operations. These operations
work faster because the unnecessary conversions are eliminated. Like the C library strcmp function,
string compare returns 0 if the strings are equal, minus 1 if the first string is lexicographically less
than the second, or 1 if the first string is greater than the second:
Example 4-1 Comparing strings with string compare.
if {[string compare $s1 $s2] == 0} {
# strings are equal
}
The string equal command added in Tcl 8.1 makes this simpler:
Example 4-2 Comparing strings with string equal.
if {[string equal $s1 $s2]} {
# strings are equal
}
String Matching
The string match command implements glob-style pattern matching that is modeled after the file
name pattern matching done by various UNIX shells.
The heritage of the word "glob" is rooted in UNIX, and Tcl preserves this historical oddity in the glob
command that does pattern matching on file names. The glob command is described on page 115.
Table 4-2 shows the three constructs used in string match patterns:
Table 4-2. Matching characters used with string match.
*
Match any number of any characters.
?
Match exactly one character.
[chars] Match any character in chars.
Any other characters in a pattern are taken as literals that must match the input exactly. The following
example matches all strings that begin with a:
string match a* alpha
=> 1
To match all two-letter strings:
string match ?? XY
=> 1
To match all strings that begin with either a or b:
string match {[ab]*}cello
=> 0
Be careful! Square brackets are also special to the Tcl interpreter, so you will need to wrap the pattern
up in curly braces to prevent it from being interpreted as a nested command. Another approach is to
put the pattern into a variable:
set pat {[ab]*x}
string match $pat box
=> 1
You can specify a range of characters with the syntax [x-y]. For example, [a-z] represents the set of
all lower-case letters, and [0-9] represents all the digits. You can include more than one range in a set.
Any letter, digit, or the underscore is matched with:
string match {[a-zA-Z0-9_]}$char
The set matches only a single character. To match more complicated patterns, like one or more
characters from a set, then you need to use regular expression matching, which is described on page
148.
If you need to include a literal *, ?, or bracket in your pattern, preface it with a backslash:
string match {*\?}what?
=> 1
In this case the pattern is quoted with curly braces because the Tcl interpreter is also doing backslash
substitutions. Without the braces, you would have to use two backslashes. They are replaced with a
single backslash by Tcl before string match is called.
string match *\\? what?
Character Classes
The string is command tests a string to see whether it belongs to a particular class. This is useful
for input validation. For example, to make sure something is a number, you do:
if {![string is integer $input]} {
error "Invalid input. Please enter a number."
}
Classes are defined in terms of the Unicode character set, which means they are more general than
specifying character sets with ranges over the ASCII encoding. For example, alpha includes many
characters outside the range of [A-Za-z] because of different characters in other alphabets. The
classes are listed in Table 4-3.
Table 4-3. Character class names.
alnum
Any alphabet or digit character.
alpha
Any alphabet character.
ascii
Any character with a 7-bit character code (i.e., less than 128.)
boolean
0, 1, true, false (in any case).
control
Character code less than 32, and not NULL.
digit
Any digit character.
double
A valid floating point number.
false
0 or false (in any case).
graph
Any printing characters, not including space characters.
integer
A valid integer.
lower
A string in all lower case.
print
A synonym for alnum.
punct
Any punctuation character.
space
Space, tab, newline, carriage return, vertical tab, backspace.
true
1 or true (in any case).
upper
A string all in upper case.
wordchar
Alphabet, digit, and the underscore.
xdigit
Valid hexadecimal digits.
Mapping Strings
The string map command translates a string based on a character map. The map is in the form of a
input, output list. Whereever a string contains an input sequence, that is replaced with the
corresponding output. For example:
string map "food" {f p d l}
=> pool
The inputs and outputs can be more than one character and do not have to be the same length:
string map "food" {f p d ll oo u}
=> pull
Example 4-3 is more practical. It uses string map to replace fancy quotes and hyphens produced by
Microsoft Word into ASCII equivalents. It uses the open, read, and close file operations that are
described in Chapter 9, and the fconfigure command described on page 223 to ensure that the file
format is UNIX friendly.
Example 4-3 Mapping Microsoft World special characters to ASCII.
proc Dos2Unix {filename} {
set input [open $filename]
set output [open $filename.new]
fconfigure $output -translation lf
puts $output [string map {
\223 "
\224 "
\222 '
\226 -
}[read $input]]
close $input
close $output
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter4. String Processing in Tcl
The append Command
The append command takes a variable name as its first argument and concatenates its remaining
arguments onto the current value of the named variable. The variable is created if it does not already
exist:
set foo z
append foo a b c
set foo
=> zabc
The append command is efficient with large strings.
The append command provides an efficient way to add items to the end of a string. It modifies a
variable directly, so it can exploit the memory allocation scheme used internally by Tcl. Using the
append command like this:
append x " some new stuff"
is always faster than this:
set x "$x some new stuff"
The lappend command described on page 61 has similar performance benefits when working with Tcl
lists.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter4. String Processing in Tcl
The format Command
The format command is similar to the C printf function. It formats a string according to a format
specification:
format spec value1 value2 ...
The spec argument includes literals and keywords. The literals are placed in the result as is, while
each keyword indicates how to format the corresponding argument. The keywords are introduced with
a percent sign, %, followed by zero or more modifiers, and terminate with a conversion specifier.
Example keywords include %f for floating point, %d for integer, and %s for string format. Use %% to
obtain a single percent character. The most general keyword specification for each argument contains
up to six parts:
position specifier
flags
field width
precision
word length
conversion character
These components are explained by a series of examples. The examples use double quotes around the
format specification. This is because often the format contains white space, so grouping is required, as
well as backslash substitutions like \t or \n, and the quotes allow substitution of these special
characters. Table 4-4 lists the conversion characters:
Table 4-4. Format conversions.
d
Signed integer.
u
Unsigned integer.
i
Signed integer. The argument may be in hex (0x) or octal (0) format.
o
Unsigned octal.
x or X
Unsigned hexadecimal. 'x' gives lowercase results.
c
Map from an integer to the ASCII character it represents.
s
A string.
f
Floating point number in the format a.b.
e or E
Floating point number in scientific notation, a.bE+-c.
g or G
Floating point number in either %f or %e format, whichever is shorter.
A position specifier is i$, which means take the value from argument i as opposed to the normally
corresponding argument. The position counts from 1. If a position is specified for one format keyword,
the position must be used for all of them. If you group the format specification with double quotes, you
need to quote the $ with a backslash:
set lang 2
format "%${lang}\$s" one un uno
=> un
The position specifier is useful for picking a string from a set, such as this simple language-specific
example. The message catalog facility described in Chapter 15 is a much more sophisticated way to
solve this problem. The position is also useful if the same value is repeated in the formatted string.
The flags in a format are used to specify padding and justification. In the following examples, the #
causes a leading 0x to be printed in the hexadecimal value. The zero in 08 causes the field to be
padded with zeros. Table 4-5 summarizes the format flag characters.
format "%#x" 20
=> 0x14
format "%#08x" 10
=> 0x0000000a
Table 4-5. Format flags.
-
Left justify the field.
+
Always include a sign, either + or -.
space Precede a number with a space, unless the number has a leading sign. Useful for packing
numbers close together.
0
Pad with zeros.
#
Leading 0 for octal. Leading 0x for hex. Always include a decimal point in floating point. Do
not remove trailing zeros (%g).
After the flags you can specify a minimum field width value. The value is padded to this width with
spaces, or with zeros if the 0 flag is used:
format "%-20s %3d" Label 2
=> Label 2
You can compute a field width and pass it to format as one of the arguments by using * as the field
width specifier. In this case the next argument is used as the field width instead of the value, and the
argument after that is the value that gets formatted.
set maxl 8
format "%-*s = %s" $maxl Key Value
=> Key = Value
The precision comes next, and it is specified with a period and a number. For %f and %e it indicates
how many digits come after the decimal point. For %g it indicates the total number of significant digits
used. For %d and %x it indicates how many digits will be printed, padding with zeros if necessary.
format "%6.2f %6.2d" 1 1
=> 1.00 01
The storage length part comes last but it is rarely useful because Tcl maintains all floating point values
in double-precision, and all integers as long words.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter4. String Processing in Tcl
The scan Command
The scan command parses a string according to a format specification and assigns values to variables.
It returns the number of successful conversions it made. The general form of the command is:
scan string format var ?var? ?var? ...
The format for scan is nearly the same as in the format command. There is no %u scan format. The %c
scan format converts one character to its decimal value.
The scan format includes a set notation. Use square brackets to delimit a set of characters. The set
matches one or more characters that are copied into the variable. A dash is used to specify a range. The
following scans a field of all lowercase letters.
scan abcABC {%[a-z]}result
=> 1
set result
=> abc
If the first character in the set is a right square bracket, then it is considered part of the set. If the first
character in the set is ^, then characters not in the set match. Again, put a right square bracket
immediately after the ^ to include it in the set. Nothing special is required to include a left square
bracket in the set. As in the previous example, you will want to protect the format with braces, or use
backslashes, because square brackets are special to the Tcl parser.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter4. String Processing in Tcl
The binary Command
Tcl 8.0 added support for binary strings. Previous versions of Tcl used null-terminated strings
internally, which foils the manipulation of some types of data. Tcl now uses counted strings, so it can
tolerate a null byte in a string value without truncating it.
This section describes the binary command that provides conversions between strings and packed
binary data representations. The binary format command takes values and packs them according to a
template. For example, this can be used to format a floating point vector in memory suitable for
passing to Fortran. The resulting binary value is returned:
binary format template value ?value ...?
The binary scan command extracts values from a binary string according to a similar template. For
example, this is useful for extracting data stored in binary format. It assigns values to a set of Tcl
variables:
binary scan value template variable ?variable ...?
Format Templates
The template consists of type keys and counts. The types are summarized in Table 4-6. In the table,
count is the optional count following the type letter.
Table 4-6. Binary conversion types.
a
A character string of length count. Padded with nulls in binary format.
A
A character string of length count. Padded with spaces in binary format. Trailing nulls and
blanks are discarded in binary scan.
b
A binary string of length count. Low-to-high order.
B
A binary string of length count. High-to-low order.
h
A hexadecimal string of length count. Low-to-high order.
H
A hexadecimal string of length count. High-to-low order. (More commonly used than h.)
c
An 8-bit character code. The count is for repetition.
s
A 16-bit integer in little-endian byte order. The count is for repetition.
S
A 16-bit integer in big-endian byte order. The count is for repetition.
i
A 32-bit integer in little-endian byte order. The count is for repetition.
I
A 32-bit integer in big-endian byte order. The count is for repetition.
f
Single-precision floating point value in native format. count is for repetition.
d
Double-precision floating point value in native format. count is for repetition.
x
Pack count null bytes with binary format.
Skip count bytes with binary scan.
X
Backup count bytes.
@
Skip to absolute position specified by count. If count is *, skip to the end.
The count is interpreted differently depending on the type. For types like integer (i) and double (d),
the count is a repetition count (e.g., i3 means three integers). For strings, the count is a length (e.g., a3
means a three-character string). If no count is specified, it defaults to 1. If count is *, then binary
scan uses all the remaining bytes in the value.
Several type keys can be specified in a template. Each key-count combination moves an imaginary
cursor through the binary data. There are special type keys to move the cursor. The x key generates
null bytes in binary format, and it skips over bytes in binary scan. The @ key uses its count as an
absolute byte offset to which to set the cursor. As a special case, @* skips to the end of the data. The X
key backs up count bytes.
Numeric types have a particular byte order that determines how their value is laid out in memory. The
type keys are lowercase for little-endian byte order (e.g., Intel) and uppercase for big-endian byte order
(e.g., SPARC and Motorola). Different integer sizes are 16-bit (s or S), 32-bit (i or I), and possibly
64-bit (l or L) on those machines that support 64-bit integers. Note that the official byte order for data
transmitted over a network is big-endian. Floating point values are always machine-specific, so it only
makes sense to format and scan these values on the same machine.
There are three string types: character (a or A), binary (b or B), and hexadecimal (h or H). With these
types the count is the length of the string. The a type pads its value to the specified length with null
bytes in binary format and the A type pads its value with spaces. If the value is too long, it is
truncated. In binary scan, the A type strips trailing blanks and nulls.
A binary string consists of zeros and ones. The b type specifies bits from low-to-high order, and the B
type specifies bits from high-to-low order. A hexadecimal string specifies 4 bits (i.e., nybbles) with
each character. The h type specifies nybbles from low-to-high order, and the H type specifies nybbles
from high-to-low order. The B and H formats match the way you normally write out numbers.
Examples
When you experiment with binary format and binary scan, remember that Tcl treats things as
strings by default. A "6", for example, is the character 6 with character code 54 or 0x36. The c type
returns these character codes:
set input 6
binary scan $input "c" 6val
set 6val
=> 54
You can scan several character codes at a time:
binary scan abc "c3" list
=> 1
set list
=> 97 98 99
The previous example uses a single type key, so binary scan sets one corresponding Tcl variable. If
you want each character code in a separate variable, use separate type keys:
binary scan abc "ccc" x y z
=> 3
set z
=> 99
Use the H format to get hexadecimal values:
binary scan 6 "H2" 6val
set 6val
=> 36
Use the a and A formats to extract fixed width fields. Here the * count is used to get all the rest of the
string. Note that A trims trailing spaces:
binary scan "hello world " a3x2A* first second
puts "\"$first\" \"$second\""
=> "hel" " world"
Use the @ key to seek to a particular offset in a value. The following command gets the second double-
precision number from a vector. Assume the vector is read from a binary data file:
binary scan $vector "@8d" double
With binary format, the a and A types create fixed width fields. A pads its field with spaces, if
necessary. The value is truncated if the string is too long:
binary format "A9A3" hello world
=> hello wor
An array of floating point values can be created with this command:
binary format "f*" 1.2 3.45 7.43 -45.67 1.03e4
Remember that floating point values are always in native format, so you have to read them on the
same type of machine that they were created. With integer data you specify either big-endian or little-
endian formats. The tcl_platform variable described on page 182 can tell you the byte order of the
current platform.
Binary Data and File I/O
When working with binary data in files, you need to turn off the newline translations and character set
encoding that Tcl performs automatically. These are described in more detail on pages 114 and 209.
For example, if you are generating binary data, the following command puts your standard output in
binary mode:
fconfigure stdout -translation binary -encoding binary
puts [binary format "B8" 11001010]
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter4. String Processing in Tcl
Related Chapters
To learn more about manipulating data in Tcl, read about lists in Chapter 5 and arrays in Chapter
8.
For more about pattern matching, read about regular expressions in Chapter 11.
For more about file I/O, see Chapter 9.
For information on Unicode and other Internationalization issues, see Chapter 15.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 5. Tcl Lists
This chapter describes Tcl lists. Tcl commands described are: list, lindex, llength, lrange,
lappend, linsert, lreplace, lsearch, lsort, concat, join, and split.
Lists in Tcl have the same structure as Tcl commands. All the rules you learned about grouping
arguments in Chapter 1 apply to creating valid Tcl lists. However, when you work with Tcl lists, it is
best to think of lists in terms of operations instead of syntax. Tcl commands provide operations to put
values into a list, get elements from lists, count the elements of lists, replace elements of lists, and so
on. The syntax can sometimes be confusing, especially when you have to group arguments to the list
commands themselves.
Lists are used with commands such as foreach that take lists as arguments. In addition, lists are
important when you are building up a command to be evaluated later. Delayed command evaluation
with eval is described in Chapter 10, and similar issues with Tk callback commands are described in
Chapter 27.
However, Tcl lists are not often the right way to build complicated data structures in scripts. You may
find Tcl arrays more useful, and they are the topic of Chapter 8. List operations are also not right for
handling unstructured data such as user input. Use regular expressions instead, which are described in
Chapter 11.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Tcl Lists
A Tcl list is a sequence of values. When you write out a list, it has the same syntax as a Tcl command.
A list has its elements separated by white space.Braces or quotes can be used to group words with
white space into a single list element. Because of the relationship between lists and commands, the
list-related commands described in this chapter are used often when constructing Tcl commands.
Big lists were often slow before Tcl 8.0.
Unlike list data structures in other languages, Tcl lists are just strings with a special interpretation. The
string representation must be parsed on each list access, so be careful when you use large lists. A list
with a few elements will not slow down your code much. A list with hundreds or thousands of
elements can be very slow. If you find yourself maintaining large lists that must be frequently
accessed, consider changing your code to use arrays instead.
The performance of lists was improved by the Tcl compiler added in Tcl 8.0. The compiler stores lists
in an internal format that requires constant time to access. Accessing the first element costs the same
as accessing any other element in the list. Before Tcl 8.0, the cost of accessing an element was
proportional to the number of elements before it in the list. The internal format also records the
number of list elements, so getting the length of a list is cheap. Before Tcl 8.0, computing the length
required reading the whole list.
Table 5-1 briefly describes the Tcl commands related to lists.
Table 5-1. List-related commands.
list arg1 arg2 ... Creates a list out of all its arguments.
lindex list i Returns the ith element from list.
llength list Returns the number of elements in list.
lrange list i j Returns the ith through jth elements from list.
lappend listVar
arg arg ...
Appends elements to the value of listVar.
linsert list index
arg arg ...
Inserts elements into list before the element at position index. Returns a
new list.
lreplace list i j
arg arg ...
Replaces elements i through j of list with the args. Returns a new list.
lsearch ?mode?
list value
Returns the index of the element in list that matches the value according to
the mode, which is -exact, -glob, or -regexp. -glob is the default. Returns
-1 if not found.
lsort ?switches?
list
Sorts elements of the list according to the switches: -ascii, -integer, -
real, -dictionary, -increasing, -decreasing, -index ix, -command
command.
Returns a new list.
concat list list
...
Joins multiple lists together into one list.
join list
joinString
Merges the elements of a list together by separating them with joinString.
split string
splitChars
Splits a string up into list elements, using the characters in splitChars as
boundaries between list elements.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Constructing Lists
Constructing a list can be tricky because you must maintain proper list syntax. In simple cases, you can
do this by hand. In more complex cases, however, you should use Tcl commands that take care of
quoting so that the syntax comes out right.
The list command
The list command constructs a list out of its arguments so that there is one list element for each
argument. If any of the arguments contain special characters, the list command adds quoting to
ensure that they are parsed as a single element of the resulting list. The automatic quoting is very
useful, and the examples in this book use the list command frequently. The next example uses list
to create a list with three values, two of which contain special characters.
Example 5-1 Constructing a list with the list command.
set x {1 2}
=> 1 2
set y foo
=> foo
set l1 [list $x "a b" $y]
=> {1 2} {a b}foo
set l2 "\{$x\\a b}$y"
=> {1 2} {a b}foo
The list command does automatic quoting.
Compare the use of list with doing the quoting by hand in Example 5-1. The assignment of l2
requires carefully constructing the first list element by using quoted braces. The braces must be turned
off so that $x can be substituted, but we need to group the result so that it remains a single list element.
We also have to know in advance that $x contains a space, so quoting is required. We are taking a risk
by not quoting $y because we know it doesn't contain spaces. If its value changes in the future, the
structure of the list can change and even become invalid. In contrast, the list command takes care of
all these details automatically.
When I first experimented with Tcl lists, I became confused by the treatment of curly braces. In the
assignment to x, for example, the curly braces disappear. However, they come back again when $x is
put into a bigger list. Also, the double quotes around a b get changed into curly braces. What's going
on? Remember that there are two steps. In the first step, the Tcl parser groups arguments. In the
grouping process, the braces and quotes are syntax that define groups. These syntax characters get
stripped off. The braces and quotes are not part of the value. In the second step, the list command
creates a valid Tcl list. This may require quoting to get the list elements into the right groups. The
list command uses curly braces to group values back into list elements.
The lappend Command
The lappend command is used to append elements to the end of a list. The first argument to lappend
is the name of a Tcl variable, and the rest of the arguments are added to the variable's value as new list
elements. Like list, lappend preserves the structure of its arguments. It may add braces to group the
values of its arguments so that they retain their identity as list elements when they are appended onto
the string representation of the list.
Example 5-2 Using lappend to add elements to a list.
lappend new 1 2
=> 1 2
lappend new 3 "4 5"
=> 1 2 3 {4 5}
set new
=> 1 2 3 {4 5}
The lappend command is unique among the list-related commands because its first argument is the
name of a list-valued variable, while all the other commands take list values as arguments. You can
call lappend with the name of an undefined variable and the variable will be created.
The lappend command is implemented efficiently to take advantage of the way that Tcl stores lists
internally. It is always more efficient to use lappend than to try and append elements by hand.
The concat Command
The concat command is useful for splicing lists together. It works by concatenating its arguments,
separating them with spaces. This joins multiple lists into one list where the top-level list elements in
each input list become top-level list elements in the resulting list:
Example 5-3 Using concat to splice lists together.
set x {4 5 6}
set y {2 3}
set z 1
concat $z $y $x
=> 1 2 3 4 5 6
Double quotes behave much like the concat command. In simple cases, double quotes behave exactly
like concat. However, the concat command trims extra white space from the end of its arguments
before joining them together with a single separating space character. Example 5-4 compares the use
of list, concat, and double quotes:
Example 5-4 Double quotes compared to the concat and list commands.
set x {1 2}
=> 1 2
set y "$x 3"
=> 1 2 3
set y [concat $x 3]
=> 1 2 3
set s { 2 }
=> 2
set y "1 $s 3"
=> 1 2 3
set y [concat 1 $s 3]
=> 1 2 3
set z [list $x $s 3]
=> {1 2} { 2 } 3
The distinction between list and concat becomes important when Tcl commands are built
dynamically. The basic rule is that list and lappend preserve list structure, while concat (or double
quotes) eliminates one level of list structure. The distinction can be subtle because there are examples
where list and concat return the same results. Unfortunately, this can lead to data-dependent bugs.
Throughout the examples of this book, you will see the list command used to safely construct lists.
This issue is discussed more in Chapter 10.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Getting List Elements: llength, lindex, and lrange
The llength command returns the number of elements in a list.
llength {a b {c d}"e f g" h}
=> 5
llength {}
=> 0
The lindex command returns a particular element of a list. It takes an index; list indices count from
zero.
set x {1 2 3}
lindex $x 1
=> 2
You can use the keyword end to specify the last element of a list, or the syntax end-N to count back
from the end of the list. The following commands are equivalent ways to get the element just before
the last element in a list.
lindex $list [expr {[llength $list] - 2}]
lindex $list end-1
The lrange command returns a range of list elements. It takes a list and two indices as arguments.
Again, end or end-N can be used as an index:
lrange {1 2 3 {4 5}}2 end
=> 3 {4 5}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Modifying Lists: linsert and lreplace
The linsert command inserts elements into a list value at a specified index. If the index is zero or
less, then the elements are added to the front. If the index is equal to or greater than the length of the
list, then the elements are appended to the end. Otherwise, the elements are inserted before the element
that is currently at the specified index.
lreplace replaces a range of list elements with new elements. If you don't specify any new elements,
you effectively delete elements from a list.
Note: linsert and lreplace do not modify an existing list. Instead, they return a new list value. In the
following example, the lreplace command does not change the value of x:
Example 5-5 Modifying lists with linsert and lreplace.
linsert {1 2}0 new stuff
=> new stuff 1 2
set x [list a {b c}e d]
=> a {b c}e d
lreplace $x 1 2 B C
=> a B C d
lreplace $x 0 0
=> {b c}e d
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Searching Lists: lsearch
lsearch returns the index of a value in the list, or -1 if it is not present. lsearch supports pattern
matching in its search. Glob-style pattern matching is the default, and this can be disabled with the -
exact flag. The semantics of glob pattern matching is described in Chapter 4. The -regexp option lets
you specify the list value with a regular expression. Regular expressions are described in Chapter 11.
In the following example, the glob pattern l* matches the value list.
lsearch {here is a list}l*
=> 3
Example 5-6 uses lreplace and lsearch to delete a list element by value. The value is found with
lsearch. The value is removed with an lreplace that does not specify any replacement list elements:
Example 5-6 Deleting a list element by value.
proc ldelete {list value } {
set ix [lsearch -exact $list $value]
if {$ix >= 0} {
return [lreplace $list $ix $ix]
} else {
return $list
}
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Sorting Lists: lsort
You can sort a list in a variety of ways with lsort. The list is not sorted in place. Instead, a new list
value is returned. The basic types of sorts are specified with the -ascii, -dictionary, -integer, or -
real options. The -increasing or -decreasing option indicate the sorting order. The default option
set is -ascii -increasing. An ASCII sort uses character codes, and a dictionary sort folds together
case and treats digits like numbers. For example:
lsort -ascii {a Z n2 n100}
=> Z a n100 n2
lsort -dictionary {a Z n2 n100}
=> a n2 n100 Z
You can provide your own sorting function for special-purpose sorting. For example, suppose you
have a list of names, where each element is itself a list containing the person's first name, middle name
(if any), and last name. The default sorts by everyone's first name. If you want to sort by their last
name, you need to supply a sorting command.
Example 5-7 Sorting a list using a comparison function.
proc NameCompare {a b} {
set alast [lindex $a end]
set blast [lindex $b end]
set res [string compare $alast $blast]
if {$res != 0} {
return $res
} else {
return [string compare $a $b]
}
}
set list {{Brent B. Welch} {John Ousterhout} {Miles Davis}}
=> {Brent B. Welch} {John Ousterhout} {Miles Davis}
lsort -command NameCompare $list
=> "not
The default separator character for split is white space, which contains spaces, tabs, and newlines. If
there are multiple separator characters in a row, these result in empty list elements; the separators are
not collapsed. The following command splits on commas, periods, spaces, and tabs. The
backslashspace sequence is used to include a space in the set of characters. You could also group the
argument to split with double quotes:
set line "\tHello, world."
split $line \,.\t
=> {}Hello {}world {}
A trick that splits each character into a list element is to specify an empty string as the split character.
This lets you get at individual characters with list operations:
split abc {}
=> a b c
However, if you write scripts that process data one character at a time, they may run slowly. Read
Chapter 11 about regular expressions for hints on really efficient string processing.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
The join Command
The join command is the inverse of split. It takes a list value and reformats it with specified
characters separating the list elements. In doing so, it removes any curly braces from the string
representation of the list that are used to group the top-level elements. For example:
join {1 {2 3} {4 5 6}}:
=> 1:2 3:4 5 6
If the treatment of braces is puzzling, remember that the first value is parsed into a list. The braces
around element values disappear in the process. Example 5-9 shows a way to implement join in a Tcl
procedure, which may help to understand the process:
Example 5-9 Implementing join in Tcl.
proc join {list sep} {
set s {} ;# s is the current separator
set result {}
foreach x $list {
append result $s $x
set s $sep
}
return $result
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter5. Tcl Lists
Related Chapters
Arrays are the other main data structure in Tcl. They are described in Chapter 8.
List operations are used when generating Tcl code dynamically. Chapter 10 describes these
techniques when using the eval command.
The foreach command loops over the values in a list. It is described on page 73 in Chapter 6.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 6. Control Structure Commands
This chapter describes the Tcl commands that implement control structures: if, switch, foreach,
while, for, break, continue, catch, error, and return.
Control structure in Tcl is achieved with commands, just like everything else. There are looping
commands: while, foreach, and for. There are conditional commands: if and switch. There is an
error handling command: catch. Finally, there are some commands to fine-tune control structures:
break, continue, return, and error.
A control structure command often has a command body that is executed later, either conditionally or
in a loop. In this case, it is important to group the command body with curly braces to avoid
substitutions at the time the control structure command is invoked. Group with braces, and let the
control structure command trigger evaluation at the proper time. A control structure command returns
the value of the last command it chose to execute.
Another pleasant property of curly braces is that they group things together while including newlines.
The examples use braces in a way that is both readable and convenient for extending the control
structure commands across multiple lines.
Commands like if, for, and while involve boolean expressions. They use the expr command
internally, so there is no need for you to invoke expr explicitly to evaluate their boolean test
expressions.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
If Then Else
The if command is the basic conditional command. If an expression is true, then execute one
command body; otherwise, execute another command body. The second command body (the else
clause) is optional. The syntax of the command is:
if expression ?then? body1 ?else? ?body2?
The then and else keywords are optional. In practice, I omit then but use else as illustrated in the
next example. I always use braces around the command bodies, even in the simplest cases:
Example 6-1 A conditional if then else command.
if {$x == 0} {
puts stderr "Divide by zero!"
} else {
set slope [expr $y/$x]
}
Curly brace positioning is important.
The style of this example takes advantage of the way the Tcl interpreter parses commands. Recall that
newlines are command terminators, except when the interpreter is in the middle of a group defined by
braces or double quotes. The stylized placement of the opening curly brace at the end of the first and
third lines exploits this property to extend the if command over multiple lines.
The first argument to if is a boolean expression. As a matter of style this expression is grouped with
curly braces. The expression evaluator performs variable and command substitution on the expression.
Using curly braces ensures that these substitutions are performed at the proper time. It is possible to be
lax in this regard, with constructs such as:
if $x break continue
This is a sloppy, albeit legitimate, if command that will either break out of a loop or continue with the
next iteration depending on the value of variable x. This style is fragile and error prone. Instead,
always use braces around the command bodies to avoid trouble later when you modify the command.
The following is much better (use then if it suits your taste):
if {$x} {
break
} else {
continue
}
When you are testing the result of a command, you can get away without using curly braces around the
command, like this:
if [command] body1
However, it turns out that you can execute the if statement more efficiently if you always group the
expression with braces, like this:
if {[command]}body1
You can create chained conditionals by using the elseif keyword. Again, note the careful placement
of curly braces that create a single if command:
Example 6-2 Chained conditional with elseif.
if {$key < 0} {
incr range 1
} elseif {$key == 0} {
return $range
} else {
incr range -1
}
Any number of conditionals can be chained in this manner. However, the switch command provides a
more powerful way to test multiple conditions.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
Switch
The switch command is used to branch to one of many command bodies depending on the value of an
expression. The choice can be made on the basis of pattern matching as well as simple comparisons.
Pattern matching is discussed in more detail in Chapter 4 and Chapter 11. The general form of the
command is:
switch flags value pat1 body1 pat2 body2 ...
Any number of pattern-body pairs can be specified. If multiple patterns match, only the body of the
first matching pattern is evaluated. You can also group all the pattern-body pairs into one argument:
switch flags value {pat1 body1 pat2 body2 ... }
The first form allows substitutions on the patterns but will require backslashes to continue the
command onto multiple lines. This is shown in Example 6-4 on page 72. The second form groups all
the patterns and bodies into one argument. This makes it easy to group the whole command without
worrying about newlines, but it suppresses any substitutions on the patterns. This is shown in Example
6-3. In either case, you should always group the command bodies with curly braces so that substitution
occurs only on the body with the pattern that matches the value.
There are four possible flags that determine how value is matched.
-exact
Matches the value exactly to one of the patterns. This is the default.
-glob
Uses glob-style pattern matching. See page 48.
-regexp
Uses regular expression pattern matching. See page 134.
--
No flag (or end of flags). Necessary when value can begin with -.
The switch command raises an error if any other flag is specified or if the value begins with -. In
practice I always use the -- flag before value so that I don't have to worry about that problem.
If the pattern associated with the last body is default, then this command body is executed if no other
patterns match. The default keyword works only on the last pattern-body pair. If you use the default
pattern on an earlier body, it will be treated as a pattern to match the literal string default:
Example 6-3 Using switch for an exact match.
switch -exact -- $value {
foo { doFoo; incr count(foo) }
bar { doBar; return $count(foo)}
default { incr count(other) }
}
If you have variable references or backslash sequences in the patterns, then you cannot use braces
around all the pattern-body pairs. You must use backslashes to escape the newlines in the command:
Example 6-4 Using switch with substitutions in the patterns.
switch -regexp -- $value \
^$key { body1 }\
\t### { body2 }\
{[0-9]*} { body3 }
In this example, the first and second patterns have substitutions performed to replace $key with its
value and \t with a tab character. The third pattern is quoted with curly braces to prevent command
substitution; square brackets are part of the regular expression syntax, too. (See page Chapter 11.)
If the body associated with a pattern is just a dash, -, then the switch command "falls through" to the
body associated with the next pattern. You can tie together any number of patterns in this manner.
Example 6-5 A switch with "fall through" cases.
switch -glob -- $value {
X* -
Y* { takeXorYaction $value }
}
Comments in switch Commands
A comment can occur only where the Tcl parser expects a command to begin.
This restricts the location of comments in a switch command. You must put them
inside the command body associated with a pattern, as shown in Example 6-6. If
you put a comment at the same level as the patterns, the switch command will try
to interpret the comment as one or more pattern-body pairs.
Example 6-6 Comments in switch commands.
switch -- $value {
# this comment confuses switch
pattern { # this comment is ok }
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
While
The while command takes two arguments, a test and a command body:
while booleanExpr body
The while command repeatedly tests the boolean expression and then executes the body if the
expression is true (nonzero). Because the test expression is evaluated again before each iteration of the
loop, it is crucial to protect the expression from any substitutions before the while command is
invoked. The following is an infinite loop (see also Example 1-13 on page 12):
set i 0 ; while $i<10 {incr i}
The following behaves as expected:
set i 0 ; while {$i<10} {incr i}
It is also possible to put nested commands in the boolean expression. The following example uses
gets to read standard input. The gets command returns the number of characters read, returning -1
upon end of file. Each time through the loop, the variable line contains the next line in the file:
Example 6-7 A while loop to read standard input.
set numLines 0 ; set numChars 0
while {[gets stdin line] >= 0} {
incr numLines
incr numChars [string length $line]
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
Foreach
The foreach command loops over a command body assigning one or more loop variables to each of
the values in one or more lists. Multiple loop variables were introduced in Tcl 7.5. The syntax for the
simple case of a single variable and a single list is:
foreach loopVar valueList commandBody
The first argument is the name of a variable, and the command body is executed once for each element
in the list with the loop variable taking on successive values in the list. The list can be entered
explicitly, as in the next example:
Example 6-8 Looping with foreach.
set i 1
foreach value {1 3 5 7 11 13 17 19 23} {
set i [expr $i*$value]
}
set i
=> 111546435
It is also common to use a list-valued variable or command result instead of a static list value. The
next example loops through command-line arguments. The variable argv is set by the Tcl interpreter
to be a list of the command-line arguments given when the interpreter was started:
Example 6-9 Parsing command-line arguments.
# argv is set by the Tcl shells
# possible flags are:
# -max integer
# -force
# -verbose
set state flag
set force 0
set verbose 0
set max 10
foreach arg $argv {
switch -- $state {
flag {
switch -glob -- $arg {
-f* {set force 1}
-v* {set verbose 1}
-max {set state max}
default {error "unknown flag $arg"}
}
}
max {
set max $arg
set state flag
}
}
}
The loop uses the state variable to keep track of what is expected next, which in this example is
either a flag or the integer value for -max. The -- flag to switch is required in this example because
the switch command complains about a bad flag if the pattern begins with a - character. The -glob
option lets the user abbreviate the -force and -verbose options.
If the list of values is to contain variable values or command results, then the list
command should be used to form the list. Avoid double quotes because if any
values or command results contain spaces or braces, the list structure will be
reparsed, which can lead to errors or unexpected results.
Example 6-10 Using list with foreach.
foreach x [list $a $b [foo]] {
puts stdout "x = $x"
}
The loop variable x will take on the value of a, the value of b, and the result of the foo command,
regardless of any special characters or whitespace in those values.
Multiple Loop Variables
You can have more than one loop variable with foreach. Suppose you have two loop variables x and
y. In the first iteration of the loop, x gets the first value from the value list and y gets the second value.
In the second iteration, x gets the third value and y gets the fourth value. This continues until there are
no more values. If there are not enough values to assign to all the loop variables, the extra variables get
the empty string as their value.
Example 6-11 Multiple loop variables with foreach.
foreach {key value} {orange 55 blue 72 red 24 green} {
puts "$key: $value"
}
orange: 55
blue: 72
red: 24
green:
If you have a command that returns a short list of values, then you can abuse the foreach command to
assign the results of the commands to several variables all at once. For example, suppose the command
MinMax returns two values as a list: the minimum and maximum values. Here is one way to get the
values:
set result [MinMax $list]
set min [lindex $result 0]
set max [lindex $result 1]
The foreach command lets us do this much more compactly:
foreach {min max}[MinMax $list] {break}
The break in the body of the foreach loop guards against the case where the command returns more
values than we expected. This trick is encapsulated into the lassign procedure in Example 10-4 on
page 131.
Multiple Value Lists
The foreach command has the ability to loop over multiple value lists in parallel. In this case, each
value list can also have one or more variables. The foreach command keeps iterating until all values
are used from all value lists. If a value list runs out of values before the last iteration of the loop, its
corresponding loop variables just get the empty string for their value.
Example 6-12 Multiple value lists with foreach.
foreach {k1 k2} {orange blue red green black}value {55 72 24} {
puts "$k1 $k2: $value"
}
orange blue: 55
red green: 72
black : 24
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
For
The for command is similar to the C for statement. It takes four arguments:
for initial test final body
The first argument is a command to initialize the loop. The second argument is a boolean expression
that determines whether the loop body will execute. The third argument is a command to execute after
the loop body:
Example 6-13 A for loop.
for {set i 0} {$i < 10} {incr i 3} {
lappend aList $i
}
set aList
=> 0 3 6 9
You could use for to iterate over a list, but you should really use foreach instead. Code like the
following is slow and cluttered:
for {set i 0} {$i < [llength $list]} {incr i} {
set value [lindex $list $i]
}
This is the same as:
foreach value $list {
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
Break and Continue
You can control loop execution with the break and continue commands. The break command causes
immediate exit from a loop, while the continue command causes the loop to continue with the next
iteration. There is no goto command in Tcl.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter6. Control Structure Commands
Catch
Until now we have ignored the possibility of errors. In practice, however, a command will raise an
error if it is called with the wrong number of arguments, or if it detects some error condition particular
to its implementation. An uncaught error aborts execution of a script.
[*]
The catch command is used
to trap such errors. It takes two arguments:
[*]
More precisely, the Tcl script unwinds and the current Tcl_Eval procedure in the C runtime library returns TCL_ERROR. There are three
cases. In interactive use, the Tcl shell prints the error message. In Tk, errors that arise during event handling trigger a call to bgerror, a Tcl
procedure you can implement in your application. In your own C code, you should check the result of Tcl_Eval and take appropriate action
in the case of an error.
catch command ?resultVar?
The first argument to catch is a command body. The second argument is the name of a variable that
will contain the result of the command, or an error message if the command raises an error. catch
returns zero if there was no error caught, or a nonzero error code if it did catch an error.
You should use curly braces to group the command instead of double quotes because catch invokes
the full Tcl interpreter on the command. If double quotes are used, an extra round of substitutions
occurs before catch is even called. The simplest use of catch looks like the following:
catch {command }
A more careful catch phrase saves the result and prints an error message:
Example 6-14 A standard catch phrase.
if {[catch { command arg1 arg2 ... }result]} {
puts stderr $result
} else {
The return, break, and continue code options take effect in the caller of the procedure doing the
exceptional return. If -code return is specified, then the calling procedure returns. If -code break is
specified, then the calling procedure breaks out of a loop, and if -code continue is specified, then the
calling procedure continues to the next iteration of the loop. These -code options to return enable the
construction of new control structures entirely in Tcl. The following example implements the break
command with a Tcl procedure:
proc break {} {
return -code break
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 7. Procedures and Scope
Procedures encapsulate a set of commands, and they introduce a local scope for variables. Commands
described are: proc, global, and upvar.
Procedures parameterize a commonly used sequence of commands. In addition, each procedure has a
new local scope for variables. The scope of a variable is the range of commands over which it is
defined. Originally, Tcl had one global scope for shared variables, local scopes within procedures, and
one global scope for procedures. Tcl 8.0 added namespaces that provide new scopes for procedures
and global variables. For simple applications you can ignore namespaces and just use the global scope.
Namespaces are described in Chapter 14.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter7. Procedures and Scope
The proc Command
A Tcl procedure is defined with the proc command. It takes three arguments:
proc name params body
The first argument is the procedure name, which is added to the set of commands understood by the
Tcl interpreter. The name is case sensitive and can contain any characters. Procedure names do not
conflict with variable names. The second argument is a list of parameter names. The last argument is
the body of the procedure.
Once defined, a Tcl procedure is used just like any other Tcl command. When it is called, each
argument is assigned to the corresponding parameter and the body is evaluated. The result of the
procedure is the result returned by the last command in the body. The return command can be used to
return a specific value.
Procedures can have default parameters so that the caller can leave out some of the command
arguments. A default parameter is specified with its name and default value, as shown in the next
example:
Example 7-1 Default parameter values.
proc P2 {a {b 7} {c -2}} {
expr $a / $b + $c
}
P2 6 3
=> 0
Here the procedure P2 can be called with one, two, or three arguments. If it is called with only one
argument, then the parameters b and c take on the values specified in the proc command. If two
arguments are provided, then only c gets the default value, and the arguments are assigned to a and b.
At least one argument and no more than three arguments can be passed to P2.
A procedure can take a variable number of arguments by specifying the args keyword as the last
parameter. When the procedure is called, the args parameter is a list that contains all the remaining
values:
Example 7-2 Variable number of arguments.
proc ArgTest {a {b foo}args} {
foreach param {a b args} {
puts stdout "\t$param = [set $param]"
}
}
set x one
set y {two things}
set z \[special\$
ArgTest $x
=> a = one
b = foo
args =
ArgTest $y $z
=> a = two things
b = [special$
args =
ArgTest $x $y $z
=> a = one
b = two things
args = {[special$}
ArgTest $z $y $z $x
=> a = [special$
b = two things
args = {[special$}one
The effect of the list structure in args is illustrated by the treatment of variable z in Example 7-2. The
value of z has special characters in it. When $z is passed as the value of parameter b, its value comes
through to the procedure unchanged. When $z is part of the optional parameters, quoting is
automatically added to create a valid Tcl list as the value of args. Example 10-3 on page 127
illustrates a technique that uses eval to undo the effect of the added list structure.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter7. Procedures and Scope
Changing Command Names with rename
The rename command changes the name of a command. There are two main uses for rename. The first
is to augment an existing procedure. Before you redefine it with proc, rename the existing command:
rename foo foo.orig
From within the new implementation of foo you can invoke the original command as foo.orig.
Existing users of foo will transparently use the new version.
The other thing you can do with rename is completely hide a command by renaming it to the empty
string. For example, you might not want users to execute UNIX programs, so you could disable exec
with the following command:
rename exec {}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter7. Procedures and Scope
Scope
By default there is a single, global scope for procedure names. This means that you can use a
procedure anywhere in your script. Variables defined outside any procedure are global variables.
However, as described below, global variables are not automatically visible inside procedures. There is
a different namespace for variables and procedures, so you could have a procedure and a global
variable with the same name without conflict. You can use the namespace facility described in Chapter
7 to manage procedures and global variables.
Each procedure has a local scope for variables. That is, variables introduced in the procedure live only
for the duration of the procedure call. After the procedure returns, those variables are undefined.
Variables defined outside the procedure are not visible to a procedure unless the upvar or global
scope commands are used. You can also use qualified names to name variables in a namespace scope.
The global and upvar commands are described later in this chapter. Qualified names are described on
page 198. If the same variable name exists in an outer scope, it is unaffected by the use of that variable
name inside a procedure.
In Example 7-3, the variable a in the global scope is different from the parameter a to P1. Similarly,
the global variable b is different from the variable b inside P1:
Example 7-3 Variable scope and Tcl procedures.
set a 5
set b -8
proc P1 {a} {
set b 42
if {$a < 0} {
return $b
} else {
return $a
}
}
P1 $b
=> 42
P1 [expr $a*2]
=> 10
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter7. Procedures and Scope
The global Command
Global scope is the toplevel scope. This scope is outside of any procedure. Variables defined at the
global scope must be made accessible to the commands inside a procedure by using the global
command. The syntax for global is:
global varName1 varName2 ...
The global command goes inside a procedure.
The global command adds a global variable to the current scope. A common mistake is to have a
single global command and expect that to apply to all procedures. However, a global command in
the global scope has no effect. Instead, you must put a global command in all procedures that access
the global variable. The variable can be undefined at the time the global command is used. When the
variable is defined, it becomes visible in the global scope.
Example 7-4 shows a random number generator. Before we look at the example, let me point out that
the best way to get random numbers in Tcl is to use the rand() math function:
expr rand()
=> .137287362934
The point of the example is to show a state variable, the seed, that has to persist between calls to
random, so it is kept in a global variable. The choice of randomSeed as the name of the global variable
associates it with the random number generator. It is important to pick names of global variables
carefully to avoid conflict with other parts of your program. For comparison, Example 14-1 on page
196 uses namespaces to hide the state variable:
Your code can pass name around as a handle on an object, then use upvar to get access to the data
associated with the object. Your code is just written to use the state variable, which is an alias to the
state variable for the current object. This technique is illustrated in Example 17-7 on page 232.
Namespaces and upvar
You can use upvar to create aliases for namespace variables, too. Namespaces are described in
Chapter 14. For example, as an alternative to reserving all global variables beginning with state, you
can use a namespace to hide these variables:
upvar #0 state::$name state
Now state is an alias to the namespace variable. This upvar trick works from inside any namespace.
Commands That Take Variable Names
Several Tcl commands involve variable names. For example, the Tk widgets can be associated with a
global Tcl variable. The vwait and tkwait commands also take variable names as arguments.
Upvar aliases do not work with text variables.
The aliases created with upvar do not work with these commands, nor do they work if you use trace,
which is described on page 183. Instead, you must use the actual name of the global variable. To
continue the above example where state is an alias, you cannot:
vwait state(foo)
button .b -textvariable state(foo)
Instead, you must
vwait state$name\(foo)
button .b -textvariable state$name\(foo)
The backslash turns off the array reference so Tcl does not try to access name as an array. You do not
need to worry about special characters in $name, except parentheses. Once the name has been passed
into the Tk widget it will be used directly as a variable name.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartI. Tcl Basics
Chapter 8. Tcl Arrays
This chapter describes Tcl arrays, which provide a flexible mechanism to build many other data
structures in Tcl. Tcl command described is: array.
An array is a Tcl variable with a string-valued index. You can think of the index as a key, and the array
as a collection of related data items identified by different keys. The index, or key, can be any string
value. Internally, an array is implemented with a hash table, so the cost of accessing each array
element is about the same. Before Tcl 8.0, arrays had a performance advantage over lists that took time
to access proportional to the size of the list.
The flexibility of arrays makes them an important tool for the Tcl programmer. A common use of
arrays is to manage a collection of variables, much as you use a C struct or Pascal record. This chapter
shows how to create several simple data structures using Tcl arrays.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter8. Tcl Arrays
Array Syntax
The index of an array is delimited by parentheses. The index can have any string value, and it can be
the result of variable or command substitution. Array elements are defined with set:
set arr(index) value
The value of an array element is obtained with $ substitution:
set foo $arr(index)
Example 8-1 uses the loop variable value $i as an array index. It sets arr(x) to the product of 1 * 2
* ... * x:
Example 8-1 Using arrays.
set arr(0) 1
for {set i 1} {$i <= 10} {incr i} {
set arr($i) [expr {$i * $arr([expr $i-1])}]
}
Complex Indices
An array index can be any string, like orange, 5, 3.1415, or foo,bar. The examples in this chapter,
and in this book, often use indices that are pretty complex strings to create flexible data structures. As
a rule of thumb, you can use any string for an index, but avoid using a string that contains spaces.
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
Running Programs with exec
The exec command runs programs from your Tcl script.
[*]
For example:
[*]
Unlike other UNIX shell exec commands, the Tcl exec does not replace the current process with the new one. Instead, the Tcl library
forks first and executes the program as a child process.
set d [exec date]
The standard output of the program is returned as the value of the exec command. However, if the
program writes to its standard error channel or exits with a nonzero status code, then exec raises an
error. If you do not care about the exit status, or you use a program that insists on writing to standard
error, then you can use catch to mask the errors:
catch {exec program arg arg} result
The exec command supports a full set of I/O redirection and pipeline syntax. Each process normally
has three I/O channels associated with it: standard input, standard output, and standard error. With I/O
redirection, you can divert these I/O channels to files or to I/O channels you have opened with the Tcl
open command. A pipeline is a chain of processes that have the standard output of one command
hooked up to the standard input of the next command in the pipeline. Any number of programs can be
linked together into a pipeline.
Example 9-1 Using exec on a process pipeline.
set n [exec sort < /etc/passwd | uniq | wc -l 2> /dev/null]
Example 9-1 uses exec to run three programs in a pipeline. The first program is sort, which takes its
input from the file /etc/passwd. The output of sort is piped into uniq, which suppresses duplicate
lines. The output of uniq is piped into wc, which counts the lines. The error output of the command is
diverted to the null device to suppress any error messages. Table 9-1 provides a summary of the syntax
understood by the exec command.
Table 9-1. Summary of the exec syntax for I/O redirection.
-keepnewline
(First argument.) Do not discard trailing newline from the result.
|
Pipes standard output from one process into another.
|&
Pipes both standard output and standard error output.
< fileName Takes input from the named file.
<@ fileId Takes input from the I/O channel identified by fileId.
<< value Takes input from the given value.
> fileName Overwrites fileName with standard output.
2> fileName Overwrites fileName with standard error output.
>& fileName Overwrites fileName with both standard error and standard out.
>> fileName Appends standard output to the named file.
2>> fileName Appends standard error to the named file.
>>& fileName Appends both standard error and standard output to the named file.
>@ fileId Directs standard output to the I/O channel identified by fileId.
2>@ fileId Directs standard error to the I/O channel identified by fileId.
>&@ fileId Directs both standard error and standard output to the I/O channel.
&
As the last argument, indicates pipeline should run in background.
A trailing & causes the program to run in the background. In this case, the process identifier is returned
by the exec command. Otherwise, the exec command blocks during execution of the program, and the
standard output of the program is the return value of exec. The trailing newline in the output is
trimmed off, unless you specify -keepnewline as the first argument to exec.
If you look closely at the I/O redirection syntax, you'll see that it is built up from a few basic building
blocks. The basic idea is that | stands for pipeline, > for output, and < for input. The standard error is
joined to the standard output by &. Standard error is diverted separately by using 2>. You can use your
own I/O channels by using @.
The auto_noexec Variable
The Tcl shell programs are set up during interactive use to attempt to execute unknown Tcl commands
as programs. For example, you can get a directory listing by typing:
ls
instead of:
exec ls
This is handy if you are using the Tcl interpreter as a general shell. It can also cause unexpected
behavior when you are just playing around. To turn this off, define the auto_noexec variable:
set auto_noexec anything
Limitations of exec on Windows
Windows 3.1 has an unfortunate combination of special cases that stem from console-mode programs,
16-bit programs, and 32-bit programs. In addition, pipes are really just simulated by writing output
from one process to a temporary file and then having the next process read from that file. If exec or a
process pipeline fails, it is because of a fundamental limitation of Windows. The good news is that
Windows 95 and Windows NT cleaned up most of the problems with exec. Windows NT 4.0 is the
most robust.
Tcl 8.0p2 was the last release to officially support Windows 3.1. That release includes Tcl1680.dll,
which is necessary to work with the win32s subsystem. If you copy that file into the same directory as
the other Tcl DLLs, you may be able to use later releases of Tcl on Windows 3.1. However, there is no
guarantee this trick will continue to work.
AppleScript on Macintosh
The exec command is not provided on the Macintosh. Tcl ships with an AppleScript extension that
lets you control other Macintosh applications. You can find documentation in the AppleScript.html
that goes with the distribution. You must use package require to load the AppleScript command:
package require Tclapplescript
AppleScript junk
=> bad option "junk": must be compile, decompile, delete,
execute, info, load, run, or store.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
The file Command
The file command provides several ways to check the status of files in the file system. For example,
you can find out if a file exists, what type of file it is, and other file attributes. There are facilities for
manipulating files in a platform-independent manner. Table 9-2 provides a summary of the various
forms of the file command. They are described in more detail later. Note that the split, join, and
pathtype operations were added in Tcl 7.5. The copy, delete, mkdir, and rename operations were
added in Tcl 7.6. The attributes operation was added in Tcl 8.0
Table 9-2. The file command options.
file atime name Returns access time as a decimal string.
file attributes name ?
option? ?value? ...
Queries or sets file attributes. (Tcl 8.0)
file copy ?-force? source
destination
Copies file source to file destination. The source and
destination can be directories. (Tcl 7.6)
file delete ?-force? name Deletes the named file. (Tcl 7.6)
file dirname name Returns parent directory of file name.
file executable name Returns 1 if name has execute permission, else 0.
file exists name Returns 1 if name exists, else 0.
file extension name Returns the part of name from the last dot (i.e., .) to the end. The
dot is included in the return value.
file isdirectory name Returns 1 if name is a directory, else 0.
file isfile name Returns 1 if name is not a directory, symbolic link, or device, else 0.
file join path path... Joins pathname components into a new pathname. (Tcl 7.5)
file lstat name var Places attributes of the link name into var.
syntax for the current platform. You can construct these names with file join described later. You
can also convert a UNIX-like name to a native name with file nativename.
Several of the file operations operate on pathnames as opposed to returning information about the
file itself. You can use the dirname, extension, join, pathtype, rootname, split, and tail
operations on any string; there is no requirement that the pathnames refer to an existing file.
Building up Pathnames: file join
You can get into trouble if you try to construct file names by simply joining components with a slash.
If part of the name is in native format, joining things with slashes will result in incorrect pathnames on
Macintosh and Windows. The same problem arises when you accept user input. The user is likely to
provide file names in native format. For example, this construct will not create a valid pathname on
the Macintosh because $tcl_library is in native format:
set file $tcl_library/init.tcl
Use file join to construct file names.
The platform-independent way to construct file names is with file join. The following command
returns the name of the init.tcl file in native format:
set file [file join $tcl_library init.tcl]
The file join operation can join any number of pathname components. In addition, it has the feature
that an absolute pathname overrides any previous components. For example (on UNIX), /b/c is an
absolute pathname, so it overrides any paths that come before it in the arguments to file join:
file join a b/c d
=> a/b/c/d
file join a /b/c d
=> /b/c/d
On Macintosh, a relative pathname starts with a colon, and an absolute pathname does not. To specify
an absolute path, you put a trailing colon on the first component so that it is interpreted as a volume
specifier. These relative components are joined into a relative pathname:
file join a :b:c d
=> :a:b:c:d
In the next case, b:c is an absolute pathname with b: as the volume specifier. The absolute name
overrides the previous relative name:
file join a b:c d
=> b:c:d
The file join operation converts UNIX-style pathnames to native format. For example, on Macintosh
you get this:
file join /usr/local/lib
=> usr:local:lib
Chopping Pathnames: split, dirname, tail
The file split command divides a pathname into components. It is the inverse of file join. The
split operation detects automatically if the input is in native or UNIX format. The results of file
split may contain some syntax to help resolve ambiguous cases when the results are passed back to
file join. For example, on Macintosh a UNIX-style pathname is split on slash separators. The
Macintosh syntax for a volume specifier (Disk:) is returned on the leading component:
file split "/Disk/System Folder/Extensions"
=> Disk: {System Folder} Extensions
A common reason to split up pathnames is to divide a pathname into the directory part and the file
part. This task is handled directly by the dirname and tail operations. The dirname operation returns
the parent directory of a pathname, while tail returns the trailing component of the pathname:
file dirname /a/b/c
=> /a/b
file tail /a/b/c
=> c
For a pathname with a single component, the dirname option returns ".", on UNIX and Windows, or
":" on Macintosh. This is the name of the current directory.
The extension and root options are also complementary. The extension option returns everything
from the last period in the name to the end (i.e., the file suffix including the period.) The root option
returns everything up to, but not including, the last period in the pathname:
file root /a/b.c
=> /a/b
file extension /a/b.c
=> .c
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
Manipulating Files and Directories
Tcl 7.6 added file operations to copy files, delete files, rename files, and create directories. In earlier
versions it was necessary to exec other programs to do these things, except on Macintosh, where cp,
rm, mv, mkdir, and rmdir were built in. These commands are no longer supported on the Macintosh.
Your scripts should use the file command operations described below to manipulate files in a
platform-independent way.
File name patterns are not directly supported by the file operations. Instead, you can use the glob
command described on page 115 to get a list of file names that match a pattern.
Copying Files
The file copy operation copies files and directories. The following example copies file1 to file2.
If file2 already exists, the operation raises an error unless the -force option is specified:
file copy ?-force? file1 file2
Several files can be copied into a destination directory. The names of the source files are preserved.
The -force option indicates that files under directory can be replaced:
file copy ?-force? file1 file2 ... directory
Directories can be recursively copied. The -force option indicates that files under dir2 can be
replaced:
file copy ?-force? dir1 dir2
Creating Directories
atime
The last access time, in seconds.
ctime
The last change time (not the create time), in seconds.
dev
The device identifier, an integer.
gid
The group owner, an integer.
ino
The file number (i.e., inode number), an integer.
mode
The permission bits.
mtime
The last modify time, in seconds.
nlink
The number of links, or directory references, to the file.
size
The number of bytes in the file.
type
file, directory, characterSpecial, blockSpecial, fifo, link, or socket.
uid
The owner's user ID, an integer.
Example 9-3 uses the device (dev) and inode (ino) attributes of a file to determine whether two
pathnames reference the same file. The attributes are UNIX specific; they are not well defined on
Windows and Macintosh.
Example 9-3 Determining whether pathnames reference the same file.
proc fileeq { path1 path2 } {
file stat $path1 stat1
file stat $path2 stat2
expr $stat1(ino) == $stat2(ino) && \
$stat1(dev) == $stat2(dev)
}
The file attributes operation was added in Tcl 8.0 to provide access to platform-specific
attributes. The attributes operation lets you set and query attributes. The interface uses option-value
pairs. With no options, all the current values are returned.
file attributes book.doc
=> -creator FRAM -hidden 0 -readonly 0 -type MAKR
These Macintosh attributes are explained in Table 9-4. The four-character type codes used on
Macintosh are illustrated on page 516. With a single option, only that value is returned:
file attributes book.doc -readonly
=> 0
The attributes are modified by specifying one or more optionvalue pairs. Setting attributes can raise
an error if you do not have the right permissions:
file attributes book.doc -readonly 1 -hidden 0
Table 9-4. Platform-specific file attributes.
-permissions
mode
File permission bits. mode is a number with bits defined by the chmod system
call. (UNIX)
-group ID The group owner of the file. (UNIX)
-owner ID The owner of the file. (UNIX)
-archive bool The archive bit, which is set by backup programs. (Windows)
-hidden bool If set, then the file does not appear in listings. (Windows, Macintosh)
-readonly bool If set, then you cannot write the file. (Windows, Macintosh)
-system bool If set, then you cannot remove the file. (Windows)
-creator type type is 4-character code of creating application. (Macintosh)
-type type type is 4-character type code. (Macintosh)
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
Input/Output Command Summary
The following sections describe how to open, read, and write files. The basic model is that you open a
file, read or write it, then close the file. Network sockets also use the commands described here.
Socket programming is discussed in Chapter 17, and more advanced event-driven I/O is described in
Chapter 16. Table 9-5 lists the basic commands associated with file I/O:
Table 9-5. Tcl commands used for file access.
open what ?access? ?permissions? Returns channel ID for a file or pipeline.
puts ?-nonewline? ?channel?
string
Writes a string.
gets channel ?varname? Reads a line.
read channel ?numBytes? Reads numBytes bytes, or all data.
read -nonewline channel Reads all bytes and discard the last \n.
tell channel Returns the seek offset.
seek channel offset ?origin? Sets the seek offset. origin is one of start, current, or
end.
eof channel Queries end-of-file status.
flush channel Writes buffers of a channel.
close channel Closes an I/O channel.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
Opening Files for I/O
The open command sets up an I/O channel to either a file or a pipeline of processes. The return value
of open is an identifier for the I/O channel. Store the result of open in a variable and use the variable as
you used the stdout, stdin, and stderr identifiers in the examples so far. The basic syntax is:
open what ?access? ?permissions?
The what argument is either a file name or a pipeline specification similar to that used by the exec
command. The access argument can take two forms, either a short character sequence that is
compatible with the fopen library routine, or a list of POSIX access flags. Table 9-6 summarizes the
first form, while Table 9-7 summarizes the POSIX flags. If access is not specified, it defaults to read.
Example 9-4 Opening a file for writing.
set fileId [open /tmp/foo w 0600]
puts $fileId "Hello, foo!"
close $fileId
Table 9-6. Summary of the open access arguments.
r
Opens for reading. The file must exist.
r+
Opens for reading and writing. The file must exist.
w
Opens for writing. Truncate if it exists. Create if it does not exist.
w+
Opens for reading and writing. Truncate or create.
a
Opens for writing. Data is appended to the file.
a+
Opens for reading and writing. Data is appended.
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
Matching File Names with glob
The glob command expands a pattern into the set of matching file names. The general form of the
glob command is:
glob ?flags? pattern ?pattern? ...
The pattern syntax is similar to the string match patterns:
* matches zero or more characters.
? matches a single character.
[abc] matches a set of characters.
{a,b,c} matches any of a, b, or c.
All other characters must match themselves.
The -nocomplain flag causes glob to return an empty list if no files match the pattern. Otherwise,
glob raises an error if no files match.
The -- flag must be used if the pattern begins with a -.
Unlike the glob matching in csh, the Tcl glob command matches only the names of existing files. In
csh, the {a,b} construct can match nonexistent names. In addition, the results of glob are not sorted.
Use the lsort command to sort its result if you find it important.
Example 9-11 shows the FindFile procedure, which traverses the file system hierarchy using
recursion. At each iteration it saves its current directory and then attempts to change to the next
subdirectory. A catch guards against bogus names. The glob command matches file names:
Example 9-11 Finding a file by name.
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
Environment Variables
Environment variables are a collection of string-valued variables associated with each process. The
process's environment variables are available through the global array env. The name of the
environment variable is the index, (e.g., env(PATH)), and the array element contains the current value
of the environment variable. If assignments are made to env, they result in changes to the
corresponding environment variable. Environment variables are inherited by child processes, so
programs run with the exec command inherit the environment of the Tcl script. The following
example prints the values of environment variables.
Example 9-12 Printing environment variable values.
proc printenv { args } {
global env
set maxl 0
if {[llength $args] == 0} {
set args [lsort [array names env]]
}
foreach x $args {
if {[string length $x] > $maxl} {
set maxl [string length $x]
}
}
incr maxl 2
foreach x $args {
puts stdout [format "%*s = %s" $maxl $x $env($x)]
}
}
printenv USER SHELL TERM
=>
USER = welch
SHELL = /bin/csh
TERM = tx
Note: Environment variables can be initialized for Macintosh applications by editing a resource of type
STR# whose name is Tcl Environment Variables. This resource is part of the tclsh and wish
applications. Follow the directions on page 28 for using ResEdit. The format of the resource values is
NAME=VALUE.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter9. Working with Files and Programs
The registry Command
Windows uses the registry to store various system configuration information. The Windows tool to
browse and edit the registry is called regedit. Tcl provides a registry command. It is a loadable
package that you must load by using:
package require registry
The registry structure has keys, value names, and typed data. The value names are stored under a key,
and each value name has data associated with it. The keys are organized into a hierarchical naming
system, so another way to think of the value names is as an extra level in the hierarchy. The main point
is that you need to specify both a key name and a value name in order to get something out of the
registry. The key names have one of the following formats:
\\hostname\rootname\keypath
rootname\keypath
rootname
The rootname is one of HKEY_LOCAL_MACHINE, HKEY_PERFORMANCE_DATA, HKEY_USERS,
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_CURRENT_CONFIG, or HKEY_DYN_DATA. Tables 9-8
and 9-9 summarize the registry command and data types:
Table 9-8. The registry command.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartII. Advanced Tcl
Chapter 10. Quoting Issues and Eval
This chapter describes explicit calls to the interpreter with the eval command. An extra round of
substitutions is performed that results in some useful effects. The chapter describes the quoting
problems with eval and the ways to avoid them. The uplevel command evaluates commands in a
different scope. The subst command does substitutions but no command invocation.
Dynamic evaluation makes Tcl flexible and powerful, but it can be tricky to use properly. The basic
idea is that you create a string and then use the eval command to interpret that string as a command or
a series of commands. Creating program code on the fly is easy with an interpreted language like Tcl,
and very hard, if not impossible, with a statically compiled language like C++ or Java. There are
several ways that dynamic code evaluation is used in Tcl:
In some cases, a simple procedure isn't quite good enough, and you need to glue together a
command from a few different pieces and then execute the result using eval. This often occurs
with wrappers, which provide a thin layer of functionality over existing commands.
Callbacks are script fragments that are saved and evaluated later in response to some event.
Examples include the commands associated with Tk buttons, fileevent I/O handlers, and after
timer handlers. Callbacks are a flexible way to link different parts of an application together.
You can add new control structures to Tcl using the uplevel command. For example, you can
write a function that applies a command to each line in a file or each node in a tree.
You can have a mixture of code and data, and just process the code part with the subst
command. For example, this is useful in HTML templates described in Chapter 18. There are
also some powerful combinations of subst and regsub described in Chapter 11.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter10. Quoting Issues and Eval
Constructing Code with the list Command
It can be tricky to assemble a command so that it is evaluated properly by eval. The same difficulties
apply to commands like after, uplevel, and the Tk send command, all of which have similar
properties to eval, except that the command evaluation occurs later or in a different context.
Constructing commands dynamically is a source of many problems. The worst part is that you can
write code that works sometimes but not others, which can be very confusing.
Use list when constructing commands.
The root of the quoting problems is the internal use of concat by eval and similar commands to
concatenate their arguments into one command string. The concat can lose some important list
structure so that arguments are not passed through as you expect. The general strategy to avoid these
problems is to use list and lappend to explicitly form the command callback as a single, well-
structured list.
The eval Command
The eval command results in another call to the Tcl interpreter. If you construct a command
dynamically, you must use eval to interpret it. For example, suppose we want to construct the
following command now but execute it later:
puts stdout "Hello, World!"
In this case, it is sufficient to do the following:
Even though there are only two Tcl commands to create a user interface button, we will write a
procedure that replaces the two commands with one. Our first version might be:
proc PackedButton {name txt cmd} {
button $name -text $txt -command $cmd
pack $name -side left
}
This is not a very flexible procedure. The main problem is that it hides the full power of the Tk button
command, which can really take about 20 widget configuration options, such as -background, -
cursor, -relief, and more. They are listed on page 391. For example, you can easily make a red
button like this:
button .foo -text Foo -command foo -background red
A better version of PackedButton uses args to pass through extra configuration options to the button
command. The args parameter is a list of all the extra arguments passed to the Tcl procedure. My first
attempt to use $args looked like this, but it was not correct:
proc PackedButton {name txt cmd args} {
button $name -text $txt -command $cmd $args
pack $name -side left
}
PackedButton .foo "Hello, World!" {exit} -background red
=> unknown option "-background red"
The problem is that $args is a list value, and button gets the whole list as a single argument. Instead,
button needs to get the elements of $args as individual arguments.
Use eval with $args
In this case, you can use eval because it concatenates its arguments to form a single list before
evaluation. The single list is, by definition, the same as a single Tcl command, so the button
command parses correctly. Here we give eval two lists, which it joins into one command:
eval {button $name -text $txt -command $cmd} $args
The use of the braces in this command is discussed in more detail below. We also generalize our
procedure to take some options to the pack command. This argument, pack, must be a list of packing
options. The final version of PackedButton is shown in Example 10-3:
Example 10-3 Using eval with $args.
# PackedButton creates and packs a button.
proc PackedButton {path txt cmd {pack {-side right}} args} {
eval {button $path -text $txt -command $cmd} $args
eval {pack $path} $pack
}
In PackedButton, both pack and args are list-valued parameters that are used as parts of a command.
The internal concat done by eval is perfect for this situation. The simplest call to PackedButton is:
PackedButton .new "New" { New }
The quotes and curly braces are redundant in this case but are retained to convey some type
information. The quotes imply a string label, and the braces imply a command. The pack argument
takes on its default value, and the args variable is an empty list. The two commands executed by
PackedButton are:
button .new -text New -command New
pack .new -side right
PackedButton creates a horizontal stack of buttons by default. The packing can be controlled with a
packing specification:
PackedButton .save "Save" { Save $file } {-side left}
The two commands executed by PackedButton are:
button .new -text Save -command { Save $file }
pack .new -side left
The remaining arguments, if any, are passed through to the button command. This lets the caller fine-
tune some of the button attributes:
PackedButton .quit Quit { Exit } {-side left -padx 5} \
-background red}
The two commands executed by PackedButton are:
button .quit -text Quit -command { Exit }-background red
pack .quit -side left -padx 5
You can see a difference between the pack and args argument in the call to PackedButton. You need
to group the packing options explicitly into a single argument. The args parameter is automatically
made into a list of all remaining arguments. In fact, if you group the extra button parameters, it will be
a mistake:
PackedButton .quit Quit { Exit } {-side left -padx 5} \
{-background red}
=> unknown option "-background red"
Correct Quoting with eval
What about the peculiar placement of braces in PackedButton?
eval {button $path -text $txt -command $cmd} $args
By using braces, we control the number of times different parts of the command are seen by the Tcl
evaluator. Without any braces, everything goes through two rounds of substitution. The braces prevent
one of those rounds. In the above command, only $args is substituted twice. Before eval is called, the
$args is replaced with its list value. Then, eval is invoked, and it concatenates its two list arguments
into one list, which is now a properly formed command. The second round of substitutions done by
eval replaces the txt and cmd values.
Do not use double quotes with eval.
You may be tempted to use double quotes instead of curly braces in your uses of eval. Don't give in!
Using double quotes is, mostly likely, wrong. Suppose the first eval command is written like this:
eval "button $path -text $txt -command $cmd $args"
Incidentally, the previous is equivalent to:
eval button $path -text $txt -command $cmd $args
These versions happen to work with the following call because txt and cmd have one-word values
with no special characters in them:
PackedButton .quit Quit { Exit }
The button command that is ultimately evaluated is:
button .quit -text Quit -command { Exit }
In the next call, an error is raised:
PackedButton .save "Save As" [list Save $file]
=> unknown option "As"
This is because the button command is this:
button .save -text Save As -command Save /a/b/c
But it should look like this instead:
button .save -text {Save As}-command {Save /a/b/c}
The problem is that the structure of the button command is now wrong. The value of txt and cmd are
substituted first, before eval is even called, and then the whole command is parsed again. The worst
part is that sometimes using double quotes works, and sometimes it fails. The success of using double
quotes depends on the value of the parameters. When those values contain spaces or special characters,
the command gets parsed incorrectly.
Braces: the one true way to group arguments to eval.
To repeat, the safe construct is:
eval {button $path -text $txt -command $cmd} $args
The following variations are also correct. The first uses list to do quoting automatically, and the
others use backslashes or braces to prevent the extra round of substitutions:
eval [list button $path -text $txt -command $cmd] $args
eval button \$path -text \$txt -command \$cmd $args
eval button {$path} -text {$txt} -command {$cmd} $args
Finally, here is one more incorrect approach that tries to quote by hand:
eval "button {$path}-text {$txt}-command {$cmd} $args"
The problem above is that quoting is not always done with curly braces. If a value contains an
unmatched curly brace, Tcl would have used backslashes to quote it, and the above command would
raise an error:
set blob "foo\{bar space"
=> foo{bar space
eval "puts {$blob}"
=> missing close brace
eval puts {$blob}
=> foo{bar space
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter10. Quoting Issues and Eval
The uplevel Command
The uplevel command is similar to eval, except that it evaluates a command in a different scope than
the current procedure. It is useful for defining new control structures entirely in Tcl. The syntax for
uplevel is:
uplevel ?level? command ?list1 list2 ...?
As with upvar, the level parameter is optional and defaults to 1, which means to execute the
command in the scope of the calling procedure. The other common use of level is #0, which means to
evaluate the command in the global scope. You can count up farther than one (e.g., 2 or 3), or count
down from the global level (e.g., #1 or #2), but these cases rarely make sense.
When you specify the command argument, you must be aware of any substitutions that might be
performed by the Tcl interpreter before uplevel is called. If you are entering the command directly,
protect it with curly braces so that substitutions occur in the other scope. The following affects the
variable x in the caller's scope:
uplevel {set x [expr $x + 1]}
However, the following will use the value of x in the current scope to define the value of x in the
calling scope, which is probably not what was intended:
uplevel "set x [expr $x + 1]"
If you are constructing the command dynamically, again use list. This fragment is used later in
Example 10-4:
uplevel [list foreach $args $valueList {break}]
It is common to have the command in a variable. This is the case when the command has been passed
into your new control flow procedure as an argument. In this case, you should evaluate the command
one level up. Put the level in explicitly to avoid cases where $cmd looks like a number!
uplevel 1 $cmd
Another common scenario is reading commands from users as part of an application. In this case, you
should evaluate the command at the global scope. Example 16-2 on page 220 illustrates this use of
uplevel:
uplevel #0 $cmd
If you are assembling a command from a few different lists, such as the args parameter, then you can
use concat to form the command:
uplevel [concat $cmd $args]
The lists in $cmd and $args are concatenated into a single list, which is a valid Tcl command. Like
eval, uplevel uses concat internally if it is given extra arguments, so you can leave out the explicit
use of concat. The following commands are equivalent:
uplevel [concat $cmd $args]
uplevel "$cmd $args"
uplevel $cmd $args
Example 10-4 shows list assignment using the foreach trick described on Page 75. List assignment is
useful if a command returns several values in a list. The lassign procedure assigns the list elements to
several variables. The lassign procedure hides the foreach trick, but it must use the uplevel
command so that the loop variables get assigned in the correct scope. The list command is used to
construct the foreach command that is executed in the caller's scope. This is necessary so that
$variables and $values get substituted before the command is evaluated in the other scope.
Example 10-4 lassign: list assignment with foreach.
# Assign a set of variables from a list of values.
# If there are more values than variables, they are returned.
# If there are fewer values than variables,
# the variables get the empty string.
proc lassign {valueList args} {
if {[llength $args] == 0} {
error "wrong # args: lassign list varname ?varname..?"
}
if {[llength $valueList] == 0} {
# Ensure one trip through the foreach loop
set valueList [list {}]
}
uplevel 1 [list foreach $args $valueList {break}]
return [lrange $valueList [llength $args] end]
}
Example 10-5 illustrates a new control structure with the File_Process procedure that applies a
callback to each line in a file. The call to uplevel allows the callback to be concatenated with the
line to form the command. The list command is used to quote any special characters in line, so it
appears as a single argument to the command.
Example 10-5 The File_Process procedure applies a command to each line of a file.
proc File_Process {file callback} {
set in [open $file]
while {[gets $file line] >= 0} {
uplevel 1 $callback [list $line]
}
close $in
}
What is the difference between these two commands?
uplevel 1 [list $callback $line]
uplevel 1 $callback [list $line]
The first form limits callback to be the name of the command, while the second form allows
callback to be a command prefix. Once again, what is the bug with this version?
uplevel 1 $callback $line
The arbitrary value of $line is concatenated to the callback command, and it is likely to be a
malformed command when executed.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter10. Quoting Issues and Eval
The subst Command
The subst command is useful when you have a mixture of Tcl commands, Tcl variable references,
and plain old data. The subst command looks through the data for square brackets, dollar signs, and
backslashes, and it does substitutions on those. It leaves the rest of the data alone:
set a "foo bar"
subst {a=$a date=[exec date]}
=> a=foo bar date=Thu Dec 15 10:13:48 PST 1994
The subst command does not honor the quoting effect of curly braces. It does substitutions regardless
of braces:
subst {a=$a date={[exec date]}}
=> a=foo bar date={Thu Dec 15 10:15:31 PST 1994}
You can use backslashes to prevent variable and command substitution.
subst {a=\$a date=\[exec date]}
=> a=$a date=[exec date]
You can use other backslash substitutions like \uXXXX to get Unicode characters, \n to get newlines, or
\-newline to hide newlines.
The subst command takes flags that limit the substitutions it will perform. The flags are -
nobackslashes, -nocommands, or -novariables. You can specify one or more of these flags before
the string that needs to be substituted:
subst -novariables {a=$a date=[exec date]}
=> a=$a date=Thu Dec 15 10:15:31 PST 1994
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter11. Regular Expressions
When to Use Regular Expressions
Regular expressions can seem overly complex at first. They introduce their own syntax and their own
rules, and you may be tempted to use simpler commands like string first, string range, or
string match to process your strings. However, often a single regular expression command can
replace a sequence of several string commands. Any time you can replace several Tcl commands
with one, you get a performance improvement. Furthermore, the regular expression matcher is
implemented in optimized C code, so pattern matching is fast.
The regular expression matcher does more than test for a match. It also tells you what part of your
input string matches the pattern. This is useful for picking data out of a large input string. In fact, you
can capture several pieces of data in just one match by using subexpressions. The regexp Tcl
command makes this easy by assigning the matching data to Tcl variables. If you find yourself using
string first and string range to pick out data, remember that regexp can do it in one step
instead.
The regular expression matcher is structured so that patterns are first compiled into an form that is
efficient to match. If you use the same pattern frequently, then the expensive compilation phase is
done only once, and all your matching uses the efficient form. These details are completely hidden by
the Tcl interface. If you use a pattern twice, Tcl will nearly always be able to retrieve the compiled
form of the pattern. As you can see, the regular expression matcher is optimized for lots of heavy-duty
string processing.
Avoiding a Common Problem
Group your patterns with curly braces.
One of the stumbling blocks with regular expressions is that they use some of the same special
characters as Tcl. Any pattern that contains brackets, dollar signs, or spaces must be quoted when used
in a Tcl command. In many cases you can group the regular expression with curly braces, so Tcl pays
no attention to it. However, when using Tcl 8.0 (or earlier) you may need Tcl to do backslash
substitutions on part of the pattern, and then you need to worry about quoting the special characters in
the regular expression.
Advanced regular expressions eliminate this problem because backslash substitution is now done by
the regular expression engine. Previously, to get \n to mean the newline character (or \t for tab) you
had to let Tcl do the substitution. With Tcl 8.1, \n and \t inside a regular expression mean newline
and tab. In fact, there are now about 20 backslash escapes you can use in patterns. Now more than
ever, remember to group your patterns with curly braces to avoid conflicts between Tcl and the regular
expression engine.
The patterns in the first sections of this Chapter ignore this problem. The sample expressions in Table
11-7 on page 151 are quoted for use within Tcl scripts. Most are quoted simply by putting the whole
pattern in braces, but some are shown without braces for comparison.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter11. Regular Expressions
Regular Expression Syntax
This section describes the basics of regular expression patterns, which are found in all versions of Tcl.
There are occasional references to features added by advanced regular expressions, but they are
covered in more detail starting on page 138. There is enough syntax in regular expressions that there
are five tables that summarize all the options. These tables appear together starting at page 145.
A regular expression is a sequence of the following items:
A literal character.
A matching character, character set, or character class.
A repetition quantifier.
An alternation clause.
A subpattern grouped with parentheses.
Matching Characters
Most characters simply match themselves. The following pattern matches an a followed by a b:
ab
The general wild-card character is the period, ".". It matches any single character. The following
pattern matches an a followed by any character:
a.
Remember that matches can occur anywhere within a string; a pattern does not have to match the
whole string. You can change that by using anchors, which are described on page 137.
Character Sets
The matching character can be restricted to a set of characters with the [xyz] syntax. Any of the
characters between the two brackets is allowed to match. For example, the following matches either
Hello or hello:
[Hh]ello
The matching set can be specified as a range over the character set with the [x-y] syntax. The
following matches any digit:
[0-9]
There is also the ability to specify the complement of a set. That is, the matching character can be
anything except what is in the set. This is achieved with the [^xyz] syntax. Ranges and complements
can be combined. The following matches anything except the uppercase and lowercase letters:
[^a-zA-Z]
Using special characters in character sets.
If you want a ] in your character set, put it immediately after the initial opening bracket. You do not
need to do anything special to include [ in your character set. The following matches any square
brackets or curley braces:
[][{}]
Most regular expression syntax characters are no longer special inside character sets. This means you
do not need to backslash anything inside a bracketed character set except for backslash itself. The
following pattern matches several of the syntax characters used in regular expressions:
[][+*?()|\\]
Advanced regular expressions add names and backslash escapes as shorthand for common sets of
characters like white space, alpha, alphanumeric, and more. These are described on page 139 and
listed in Table 11-3 on page 146.
Quantifiers
Repetition is specified with *, for zero or more, +, for one or more, and ?, for zero or one. These
quantifiers apply to the previous item, which is either a matching character, a character set, or a
subpattern grouped with parentheses. The following matches a string that contains b followed by zero
or more a's:
ba*
You can group part of the pattern with parentheses and then apply a quantifier to that part of the
pattern. The following matches a string that has one or more sequences of ab:
(ab)+
The pattern that matches anything, even the empty string, is:
.*
These quantifiers have a greedy matching behavior: They match as many characters as possible.
Advanced regular expressions add nongreedy matching, which is described on page 140. For example,
a pattern to match a single line might look like this:
.*\n
However, as a greedy match, this will match all the lines in the input, ending with the last newline in
the input string. The following pattern matches up through the first newline.
[^\n]*\n
We will shorten this pattern even further on page 140 by using nongreedy quantifiers. There are also
special newline sensitive modes you can turn on with some options described on page 143.
Alternation
Alternation lets you test more than one pattern at the same time. The matching engine is designed to be
able to test multiple patterns in parallel, so alternation is efficient. Alternation is specified with |, the
pipe symbol. Another way to match either Hello or hello is:
hello|Hello
You can also write this pattern as:
(h|H)ello
or as:
[hH]ello
Anchoring a Match
By default a pattern does not have to match the whole string. There can be unmatched characters
before and after the match. You can anchor the match to the beginning of the string by starting the
pattern with ^, or to the end of the string by ending the pattern with $. You can force the pattern to
match the whole string by using both. All strings that begin with spaces or tabs are matched with:
^[ \t]+
If you have many text lines in your input, you may be tempted to think of ^ as meaning "beginning of
line" instead of "beginning of string." By default, the ^ and $ anchors are relative to the whole input,
and embedded newlines are ignored. Advanced regular expressions support options that make the ^
and $ anchors line-oriented. They also add the \A and \Z anchors that always match the beginning and
end of the string, respectively.
Backslash Quoting
Use the backslash character to turn off these special characters :
. * ? + [ ] ( ) ^ $ | \
For example, to match the plus character, you will need:
\+
Remember that this quoting is not necessary inside a bracketed expression (i.e., a character set
definition.) For example, to match either plus or question mark, either of these patterns will work:
(\+|\?)
[+?]
To match a single backslash, you need two. You must do this everywhere, even inside a bracketed
expression. Or you can use \B, which was added as part of advanced regular expressions. Both of these
match a single backslash:
\\
\B
Unknown backslash sequences are an error.
Versions of Tcl before 8.1 ignored unknown backslash sequences in regular expressions. For example,
\= was just =, and \w was just w. Even \n was just n, which was probably frustrating to many
beginners trying to get a newline into their pattern. Advanced regular expressions add backslash
sequences for tab, newline, character classes, and more. This is a convenient improvement, but in rare
cases it may change the semantics of a pattern. Usually these cases are where an unneeded backslash
suddenly takes on meaning, or causes an error because it is unknown.
Matching Precedence
If a pattern can match several parts of a string, the matcher takes the match that occurs earliest in the
input string. Then, if there is more than one match from that same point because of alternation in the
pattern, the matcher takes the longest possible match. The rule of thumb is: first, then longest. This
rule gets changed by nongreedy quantifiers that prefer a shorter match.
Watch out for *, which means zero or more, because zero of anything is pretty easy to match. Suppose
your pattern is:
[a-z]*
This pattern will match against 123abc, but not how you expect. Instead of matching on the letters in
the string, the pattern will match on the zero-length substring at the very beginning of the input string!
This behavior can be seen by using the -indices option of the regexp command described on page
148. This option tells you the location of the matching string instead of the value of the matching
string.
Capturing Subpatterns
Use parentheses to capture a subpattern. The string that matches the pattern within parentheses is
remembered in a matching variable, which is a Tcl variable that gets assigned the string that matches
the pattern. Using parentheses to capture subpatterns is very useful. Suppose we want to get everything
between the <td> and </td> tags in some HTML. You can use this pattern:
<td>([^<]*)</td>
The matching variable gets assigned the part of the input string that matches the pattern inside the
parentheses. You can capture many subpatterns in one match, which makes it a very efficient way to
pick apart your data. Matching variables are explained in more detail on page 148 in the context of the
regexp command.
Sometimes you need to introduce parentheses but you do not care about the match that occurs inside
them. The pattern is slightly more efficient if the matcher does not need to remember the match.
Advanced regular expressions add noncapturing parentheses with this syntax:
(?:pattern)
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter11. Regular Expressions
Advanced Regular Expressions
The syntax added by advanced regular expressions is mostly just short hand notation for constructs
you can make with the basic syntax already described. There are also some new features that add
additional power: nongreedy quantifiers, back references, look-ahead patterns, and named character
classes. If you are just starting out with regular expressions, you can ignore most of this section, except
for the one about backslash sequences. Once you master the basics, of if you are already familar with
regular expressions in Tcl (or the UNIX vi editor or grep utility), then you may be interested in the
new features of advanced regular expressions.
Compatibility with Patterns in Tcl 8.0
Advanced regular expressions add syntax in an upward compatible way. Old patterns continue to work
with the new matcher, but advanced regular expressions will raise errors if given to old versions of
Tcl. For example, the question mark is used in many of the new constructs, and it is artfully placed in
locations that would not be legal in older versions of regular expressions. The added syntax is
summarized in Table 11-2 on page 145.
If you have unbraced patterns from older code, they are very likely to be correct in Tcl 8.1 and later
versions. For example, the following pattern picks out everything up to the next newline. The pattern
is unbraced, so Tcl substitutes the newline character for each occurrence of \n. The square brackets are
quoted so that Tcl does not think they delimit a nested commmand:
regexp "(\[^\n\]+)\n" $input
The above command behaves identically when using advanced regular expressions, although you can
now also write it like this:
regexp {([^\n]+)\n} $input
The curley braces hide the brackets from the Tcl parser, so they do not need to be escaped with
arguments are optional. If present, match is set to be the part of the string that matched the pattern. The
remaining variables are set to be the substrings of string that matched the corresponding subpatterns
in pattern. The correspondence is based on the order of left parentheses in the pattern to avoid
ambiguities that can arise from nested subpatterns.
Example 11-2 uses regexp to pick the hostname out of the DISPLAY environment variable, which has
the form:
hostname:display.screen
Example 11-2 Using regular expressions to parse a string.
set env(DISPLAY) sage:0.1
regexp {([^:]*):}$env(DISPLAY) match host
=> 1
set match
=> sage:
set host
=> sage
The pattern involves a complementary set, [^:], to match anything except a colon. It uses repetition,
*, to repeat that zero or more times. It groups that part into a subexpression with parentheses. The
literal colon ensures that the DISPLAY value matches the format we expect. The part of the string that
matches the complete pattern is stored into the match variable. The part that matches the subpattern is
stored into host. The whole pattern has been grouped with braces to quote the square brackets.
Without braces it would be:
regexp (\[^:\]*): $env(DISPLAY) match host
With advanced regular expressions the nongreedy quantifier *? can replace the complementary set:
regexp (.*?): $env(DISPLAY) match host
This is quite a powerful statement, and it is efficient. If we had only had the string command to work
with, we would have needed to resort to the following, which takes roughly twice as long to interpret:
set i [string first : $env(DISPLAY)]
if {$i >= 0} {
set host [string range $env(DISPLAY) 0 [expr $i-1]]
}
A Pattern to Match URLs
Example 11-3 demonstrates a pattern with several subpatterns that extract the different parts of a URL.
There are lots of subpatterns, and you can determine which match variable is associated with which
subpattern by counting the left parenthesis. The pattern will be discussed in more detail after the
example:
Example 11-3 A pattern to match URLs.
set url http://www.beedub.com:80/index.html
regexp {([^:]+)://([^:/]+)(:([0-9]+))?(/.*)}$url \
match protocol x serverport path
=> 1
set match
=> http://www.beedub.com:80/index.html
set protocol
=> http
set server
=> www.beedub.com
set x
=> :80
set port
=> 80
set path
=> /index.html
Let's look at the pattern one piece at a time. The first part looks for the protocol, which is separated by
a colon from the rest of the URL. The first part of the pattern is one or more characters that are not a
colon, followed by a colon. This matches the http: part of the URL:
[^:]+:
Using nongreedy +? quantifier, you could also write that as:
.+?:
The next part of the pattern looks for the server name, which comes after two slashes. The server name
is followed either by a colon and a port number, or by a slash. The pattern uses a complementary set
that specifies one or more characters that are not a colon or a slash. This matches the
//www.beedub.com part of the URL:
//[^:/]+
The port number is optional, so a subpattern is delimited with parentheses and followed by a question
mark. An additional set of parentheses are added to capture the port number without the leading colon.
This matches the :80 part of the URL:
(:([0-9]+))?
The last part of the pattern is everything else, starting with a slash. This matches the /index.html part
of the URL:
/.*
Use subpatterns to parse strings.
To make this pattern really useful, we delimit several subpatterns with parentheses:
([^:]+)://([^:/]+)(:([0-9]+))?(/.*)
These parentheses do not change the way the pattern matches. Only the optional port number really
needs the parentheses in this example. However, the regexp command gives us access to the strings
that match these subpatterns. In one step regexp can test for a valid URL and divide it into the
protocol part, the server, the port, and the trailing path.
The parentheses around the port number include the : before the digits. We've used a dummy variable
that gets the : and the port number, and another match variable that just gets the port number. By using
noncapturing parentheses in advanced regular expressions, we can eliminate the unused match
variable. We can also replace both complementary character sets with a nongreedy .+? match.
Example 11-4 shows this variation:
Example 11-4 An advanced regular expression to match URLs.
set url http://www.beedub.com:80/book/
regexp {(.+?)://(.+?)(?::([0-9]+))?(/.*)}$url \
match protocol server port path
=> 1
set match
=> http://www.beedub.com:80/book/
set protocol
=> http
set server
=> www.beedub.com
set port
=> 80
set path
=> /book/
Sample Regular Expressions
The table in this section lists regular expressions as you would use them in Tcl commands. Most are
quoted with curly braces to turn off the special meaning of square brackets and dollar signs. Other
patterns are grouped with double quotes and use backslash quoting because the patterns include
backslash sequences like \n and \t. In Tcl 8.0 and earlier, these must be substituted by Tcl before the
regexp command is called. In these cases, the equivalent advanced regular expression is also shown.
Table 11-7. Sample regular expressions.
{^[yY]}
Begins with y or Y, as in a Yes answer.
{^(yes|YES|Yes)$}
Exactly "yes", "Yes", or "YES".
"^\[^ \t:\]+:"
Begins with colon-delimited field that has no spaces or tabs.
{^\S+:}
Same as above, using \S for "not space".
"^\[ \t]*$"
A string of all spaces or tabs.
{(?n)^\s*$}
A blank line using newline sensitive mode.
"(\n|^)\[^\n\]*(\n|$)"
A blank line, the hard way.
{^[A-Za-z]+$}
Only letters.
{^[[:alpha:]]+$}
Only letters, the Unicode way.
{[A-Za-z0-9_]+}
Letters, digits, and the underscore.
{\w+}
Letters, digits, and the underscore using \w.
{[][${}\\]}
The set of Tcl special characters: ] [ $ { } \
"\[^\n\]*\n"
Everything up to a newline.
{.*?\n}
Everything up to a newline using nongreedy *?
{\.}
A period.
{[][$^?+*()|\\]}
The set of regular expression special characters: ] [ $ ^ ? + * ( ) | \
<H1>(.*?)</H1>
An H1 HTML tag. The subpattern matches the string between the tags.
<!--.*?-->
HTML comments.
{[0-9a-hA-H][0-9a-hA-H]}
2 hex digits.
{[[:xdigit:]]{2}}
2 hex digits, using advanced regular expressions.
{\d{1,3}}
1 to 3 digits, using advanced regular expressions.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter11. Regular Expressions
The regsub Command
The regsub command does string substitution based on pattern matching. It is very useful for
processing your data. It can perform simple tasks like replacing sequences of spaces and tabs with a
single space. It can perform complex data transforms, too, as described in the next section. Its syntax
is:
regsub ?switches? pattern string subspec varname
The regsub command returns the number of matches and replacements, or 0 if there was no match.
regsub copies string to varname, replacing occurrences of pattern with the substitution specified by
subspec. If the pattern does not match, then string is copied to varname without modification. The
optional switches include:
-all, which means to replace all occurrences of the pattern. Otherwise only the first occurrence
is replaced.
The -nocase, -expanded, -line, -linestop, and -lineanchor switches are the same as in the
regexp command. They are described on page 148.
The -- switch separates the pattern from the switches, which is necessary if your pattern begins
with a -.
The replacement pattern, subspec, can contain literal characters as well as the following special
sequences:
& is replaced with the string that matched the pattern.
\x , where x is a number, is replaced with the string that matched the corresponding subpattern in
pattern. The correspondence is based on the order of left parentheses in the pattern
specification.
The following replaces a user's home directory with a ~:
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartII. Advanced Tcl
Chapter 12. Script Libraries and Packages
Collections of Tcl commands are kept in libraries and organized into packages. Tcl automatically
loads libraries as an application uses their commands. Tcl commands discussed are: package,
pkg_mkIndex, auto_mkindex, unknown, and tcl_findLibrary.
Libraries group useful sets of Tcl procedures so that they can be used by multiple applications. For
example, you could use any of the code examples that come with this book by creating a script library
and then directing your application to check in that library for missing procedures. One way to
structure a large application is to have a short main script and a library of support scripts. The
advantage of this approach is that not all the Tcl code needs to be loaded to start the application.
Applications start up quickly, and as new features are accessed, the code that implements them is
loaded automatically.
The Tcl package facility supports version numbers and has a provide/require model of use. Typically,
each file in a library provides one package with a particular version number. Packages also work with
shared object libraries that implement Tcl commands in compiled code, which are described in
Chapter 44. A package can be provided by a combination of script files and object files. Applications
specify which packages they require and the libraries are loaded automatically. The package facility is
an alternative to the auto loading scheme used in earlier versions of Tcl. You can use either
mechanism, and this chapter describes them both.
If you create a package you may wish to use the namespace facility to avoid conflicts between
procedures and global variables used in different packages. Namespaces are the topic of Chapter 14.
Before Tcl 8.0 you had to use your own conventions to avoid conflicts. This chapter explains a simple
coding convention for large Tcl programs. I use this convention in exmh, a mail user interface that has
grown from about 2,000 to over 35,000 lines of Tcl code. A majority of the code has been contributed
by the exmh user community. Such growth might not have been possible without coding conventions.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
Locating Packages: The auto_path Variable
The package facility assumes that Tcl libraries are kept in well-known directories. The list of well-
known directories is kept in the auto_path Tcl variable. This is initialized by tclsh and wish to include
the Tcl script library directory, the Tk script library directory (for wish), and the parent directory of the
Tcl script library directory. For example, on my Macintosh auto_path is a list of these three
directories:
Disk:System Folder:Extensions:Tool Command Language:tcl8.2
Disk:System Folder:Extensions:Tool Command Language
Disk:System Folder:Extensions:Tool Command Language:tk8.2
On my Windows 95 machine the auto_path lists these directories:
c:\Program Files\Tcl\lib\Tcl8.2
c:\Program Files\Tcl\lib
c:\Program Files\Tcl\lib\Tk8.2
On my UNIX workstation the auto_path lists these directories:
/usr/local/tcl/lib/tcl8.2
/usr/local/tcl/lib
/usr/local/tcl/lib/tk8.2
The package facility searches these directories and their subdirectories for packages. The easiest way
to manage your own packages is to create a directory at the same level as the Tcl library:
/usr/local/tcl/lib/welchbook
Packages in this location, for example, will be found automatically because the auto_path list
includes /usr/local/tcl/lib. You can also add directories to the auto_path explicitly:
lappend auto_path directory
One trick I often use is to put the directory containing the main script into the auto_path. The
following command sets this up:
lappend auto_path [file dirname [info script]]
If your code is split into bin and lib directories, then scripts in the bin directory can add the adjacent
lib directory to their auto_path with this command:
lappend auto_path \
[file join [file dirname [info script]] ../lib]
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
Using Packages
Each script file in a library declares what package it implements with the package provide
command:
package provide name version
The name identifies the package, and the version has a major.minor format. The convention is that
the minor version number can change and the package implementation will still be compatible. If the
package changes in an incompatible way, then the major version number should change. For example,
Chapter 17 defines several procedures that use the HTTP network protocol. These include Http_Open,
Http_Get, and Http_Validate. The file that contains the procedures starts with this command:
package provide Http 1.0
Case is significant in package names. In particular, the package that comes with Tcl is named http ?all
lowercase.
More than one file can contribute to the same package simply by specifying the same name and
version. In addition, different versions of the same package can be kept in the same directory but in
different files.
An application specifies the packages it needs with the package require command:
package require name ?version? ?-exact?
If the version is left off, then the highest available version is loaded. Otherwise the highest version
with the same major number is loaded. For example, if the client requires version 1.1, version 1.2
could be loaded if it exists, but versions 1.0 and 2.0 would not be loaded. You can restrict the package
to a specific version with the -exact flag. If no matching version can be found, then the package
require command raises an error.
Put the appropriate package require and package provide commands in your code.
Ensure that your library directories, or their parent directories, are listed in the auto_path
variable.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
The package Command
The package command has several operations that are used primarily by the pkg_mkIndex procedure
and the automatic loading facility. These operations are summarized in Table 12-2.
Table 12-2. The package command.
package forget package Deletes registration information for package.
package ifneeded
package ?command?
Queries or sets the command used to set up automatic loading of a
package.
package names
Returns the set of registered packages.
package provide package
version
Declares that a script file defines commands for package with the
given version.
package require package
?version? ?-exact?
Declares that a script uses package. The -exact flag specifies that the
exact version must be loaded. Otherwise, the highest matching
version is loaded.
package unknown ?
command?
Queries or sets the command used to locate packages.
package vcompare v1 v2 Compares version v1 and v2. Returns 0 if they are equal, minus 1 if v1
is less than v2, or 1 if v1 is greater than v2.
package versions
package
Returns which versions of the package are registered.
package vsatisfies v1
v2
Returns 1 if v1 is greater or equal to v2 and still has the same major
version number. Otherwise returns 0.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
Libraries Based on the tclIndex File
You can create libraries without using the package command. The basic idea is that a directory has a
library of script files, and an index of the Tcl commands defined in the library is kept in a tclIndex
file. The drawback is that versions are not supported and you may need to adjust the auto_path to list
your library directory. The main advantage of this approach is that this mechanism has been part of Tcl
since the earliest versions. If you currently maintain a library using tclIndex files, it will still work.
You must generate the index that records what procedures are defined in the library. The
auto_mkindex procedure creates the index, which is stored in a file named tclIndex that is kept in the
script library directory. (Watch out for the difference in capitalization between auto_mkindex and
pkg_mkIndex!) Suppose all the examples from this book are in the directory
/usr/local/tcl/welchbook. You can make the examples into a script library by creating the
tclIndex file:
auto_mkindex /usr/local/tcl/welchbook *.tcl
You will need to update the tclIndex file if you add procedures or change any of their names. A
conservative approach to this is shown in the next example. It is conservative because it re-creates the
index if anything in the library has changed since the tclIndex file was last generated, whether or not
the change added or removed a Tcl procedure.
Example 12-1 Maintaining a tclIndex file.
proc Library_UpdateIndex { libdir } {
set index [file join $libdir tclIndex]
if {![file exists $index]} {
set doit 1
} else {
set age [file mtime $index]
set doit 0
# Changes to directory may mean files were deleted
if {[file mtime $libdir] > $age} {
set doit 1
} else {
# Check each file for modification
foreach file [glob [file join $libdir *.tcl]] {
if {[file mtime $file] > $age} {
set doit 1
break
}
}
}
}
if { $doit } {
auto_mkindex $libdir *.tcl
}
}
Tcl uses the auto_path variable to record a list of directories to search for unknown commands. To
continue our example, you can make the procedures in the book examples available by putting this
command at the beginning of your scripts:
lappend auto_path /usr/local/tcl/welchbook
This has no effect if you have not created the tclIndex file. If you want to be extra careful, you can
call Library_UpdateIndex. This will update the index if you add new things to the library.
lappend auto_path /usr/local/tcl/welchbook
Library_UpdateIndex /usr/local/tcl/welchbook
This will not work if there is no tclIndex file at all because Tcl won't be able to find the
implementation of Library_UpdateIndex. Once the tclIndex has been created for the first time, then
this will ensure that any new procedures added to the library will be installed into tclIndex. In
practice, if you want this sort of automatic update, it is wise to include something like the
Library_UpdateIndex procedure directly into your application as opposed to loading it from the
library it is supposed to be maintaining.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
The unknown Command
Automatic loading of Tcl commands is implemented by the unknown command. Whenever the Tcl
interpreter encounters a command that it does not know about, it calls the unknown command with the
name of the missing command. The unknown command is implemented in Tcl, so you are free to
provide your own mechanism to handle unknown commands. This chapter describes the behavior of
the default implementation of unknown, which can be found in the init.tcl file in the Tcl library. The
location of the library is returned by the info library command.
How Auto Loading Works
The unknown command uses an array named auto_index. One element of the array is defined for each
procedure that can be automatically loaded. The auto_index array is initialized by the package
mechanism or by tclIndex files. The value of an auto_index element is a command that defines the
procedure. Typical commands are:
source [file join $dir bind_ui.tcl]
load [file join $dir mime.so] Mime
The $dir gets substituted with the name of the directory that contains the library file, so the result is a
source or load command that defines the missing Tcl command. The substitution is done with eval,
so you could initialize auto_index with any commands at all. Example 12-2 is a simplified version of
the code that reads the tclIndex file.
Example 12-2 Loading a tclIndex file.
# This is a simplified part of the auto_load_index procedure.
# Go through auto_path from back to front.
set i [expr [llength $auto_path]-1]
for {} {$i >= 0} {incr i -1} {
set dir [lindex $auto_path $i]
if [catch {open [file join $dir tclIndex]} f] {
# No index
continue
}
# eval the file as a script. Because eval is
# used instead of source, an extra round of
# substitutions is performed and $dir gets expanded
# The real code checks for errors here.
eval [read $f]
close $f
}
Disabling the Library Facility: auto_noload
If you do not want the unknown procedure to try and load procedures, you can set the auto_noload
variable to disable the mechanism:
set auto_noload anything
Auto loading is quite fast. I use it regularly on applications both large and small. A large application
will start faster if you only need to load the code necessary to start it up. As you access more features
of your application, the code will load automatically. Even a small application benefits from auto
loading because it encourages you to keep commonly used code in procedure libraries.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
Interactive Conveniences
The unknown command provides a few other conveniences. These are used only when you are typing
commands directly. They are disabled once execution enters a procedure or if the Tcl shell is not being
used interactively. The convenience features are automatic execution of programs, command history,
and command abbreviation. These options are tried, in order, if a command implementation cannot be
loaded from a script library.
Auto Execute
The unknown procedure implements a second feature: automatic execution of external programs. This
makes a Tcl shell behave more like other UNIX shells that are used to execute programs. The search
for external programs is done using the standard PATH environment variable that is used by other shells
to find programs. If you want to disable the feature all together, set the auto_noexec variable:
set auto_noexec anything
History
The history facility described in Chapter 13 is implemented by the unknown procedure.
Abbreviations
If you type a unique prefix of a command, unknown recognizes it and executes the matching command
for you. This is done after automatic program execution is attempted and history substitutions are
performed.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
Tcl Shell Library Environment
Tcl searches for its script library directory when it starts up. In early versions of Tcl you had to
compile in the correct location, set a Windows registry value, or set the TCL_LIBRARY environment
variable to the correct location. Recent versions of Tcl use a standard searching scheme to locate the
script library. The search understands the standard installation and build environments for Tcl, and it
should eliminate the need to use the TCL_LIBRARY environment variable. On Windows the search
for the library used to depend on registry values, but this has also been discontinued in favor of a
standard search. In summary, "it should just work." However, this section explains how Tcl finds its
script library so that you can troubleshoot problems.
Locating the Tcl Script Library
The default library location is defined when you configure the source distribution, which is explained
on page 644. At this time an initial value for the auto_path variable is defined. (This default value
appears in tcl_pkgPath, but changing this variable has no effect once Tcl has started. I just pretend
tcl_pkgPath does not exist.) These values are just hints; Tcl may use other directories depending on
what it finds in the file system.
When Tcl starts up, it searches for a directory that contains its init.tcl startup script. You can short-
circuit the search by defining the TCL_LIBRARY environment variable. If this is defined, Tcl uses it only
for its script library directory. However, you should not need to define this with normal installations of
Tcl 8.0.5 or later. In my environment I'm often using several different versions of Tcl for various
applications and testing purposes, so setting TCL_LIBRARY is never correct for all possibilities. If I find
myself setting this environment variable, I know something is wrong with my Tcl installations!
The standard search starts with the default value that is compiled into Tcl (e.g.,
/usr/local/lib/tcl8.1.) After that, the following directories are examined for an init.tcl file.
These example values assume Tcl version 8.1 and patch level 8.1.1:
../lib/tcl8.1
../../lib/tcl8.1
../library
../../tcl8.1.1/library
../../../tcl8.1.1/library
The first two directories correspond to the standard installation directories, while the last three
correspond to the standard build environment for Tcl or Tk. The first directory in the list that contains
a valid init.tcl file becomes the Tcl script library. This directory location is saved in the
tcl_library global variable, and it is also returned by the info library command.
The primary thing defined by init.tcl is the implementation of the unknown procedure. It also
initializes auto_path to contain $tcl_library and the parent directory of $tcl_library. There may
be additional directories added to auto_path depending on the compiled in value of tcl_pkgPath.
tcl_findLibrary
A generalization of this search is implemented by tcl_findLibrary. This procedure is designed for
use by extensions like Tk and [incr Tcl]. Of course, Tcl cannot use tcl_findLibrary itself because it
is defined in init.tcl!
The tcl_findLibrary procedure searches relative to the location of the main program (e.g., tclsh or
wish) and assumes a standard installation or a standard build environment. It also supports an override
by an environment variable, and it takes care of sourcing an initialization script. The usage of
tcl_findLibrary is:
tcl_findLibrary base version patch script enVar varName
The base is the prefix of the script library directory name. The version is the main version number
(e.g., "8.0"). The patch is the full patch level (e.g., "8.0.3"). The script is the initialization script to
source from the directory. The enVar names an environment variable that can be used to override the
default search path. The varName is the name of a variable to set to name of the directory found by
tcl_findLibrary. A side effect of tcl_findLibrary is to source the script from the directory. An
example call is:
tcl_findLibrary tk 8.0 8.0.3 tk.tcl TK_LIBRARY tk_library
This call first checks to see whether TK_LIBRARY is defined in the environment. If so, it uses its value.
Otherwise, it searches the following directories for a file named tk.tcl. It sources the script and sets
the tk_library variable to the directory containing that file. The search is relative to the value
returned by info nameofexecutable:
../lib/tk8.0
../../lib/tk8.0
../library
../../tk8.0.3/library
../../../tk8.0.3/library
Tk also adds $tk_library to the end of auto_path, so the other script files in that directory are
available to the application:
lappend auto_path $tk_library
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter12. Script Libraries and Packages
Coding Style
If you supply a package, you need to follow some simple coding conventions to make your library
easier to use by other programmers. You can use the namespace facility introduced in Tcl 8.0. You can
also use conventions to avoid name conflicts with other library packages and the main application.
This section describes the conventions I developed before namespaces were added to Tcl.
A Module Prefix for Procedure Names
The first convention is to choose an identifying prefix for the procedures in your package. For
example, the preferences package in Chapter 42 uses Pref as its prefix. All the procedures provided
by the library begin with Pref. This convention is extended to distinguish between private and
exported procedures. An exported procedure has an underscore after its prefix, and it is acceptable to
call this procedure from the main application or other library packages. Examples include Pref_Add,
Pref_Init, and Pref_Dialog. A private procedure is meant for use only by the other procedures in
the same package. Its name does not have the underscore. Examples include PrefDialogItem and
PrefXres.
This naming convention precludes casual names like doit, setup, layout, and so on. Without using
namespaces, there is no way to hide procedure names, so you must maintain the naming convention
for all procedures in a package.
A Global Array for State Variables
You should use the same prefix on the global variables used by your package. You can alter the
capitalization; just keep the same prefix. I capitalize procedure names and use lowercase letters for
variables. By sticking with the same prefix you identify what variables belong to the package and you
avoid conflict with other packages.
Collect state in a global array.
In general, I try to use a single global array for a package. The array provides a convenient place to
collect a set of related variables, much as a struct is used in C. For example, the preferences package
uses the pref array to hold all its state information. It is also a good idea to keep the use of the array
private. It is better coding practice to provide exported procedures than to let other modules access
your data structures directly. This makes it easier to change the implementation of your package
without affecting its clients.
If you do need to export a few key variables from your module, use the underscore convention to
distinguish exported variables. If you need more than one global variable, just stick with the prefix
convention to avoid conflicts.
The Official Tcl Style Guide
John Ousterhout has published two programming style guides, one for C programming known as "The
Engineering Manual" and one for Tcl scripts known as "The Style Guide". These describe details
about file structure as well as naming conventions for modules, procedures, and variables. The Tcl
Style Guide conventions use Tcl namespaces to separate packages. Namespaces automatically provide
a way to avoid conflict between procedure names. Namespaces also support collections of variables
without having to use arrays for grouping.
You can find these style guides on the CD-ROM and also in ftp://ftp.scriptics.com/pub/tcl/doc. The
Engineering Manual is distributed as a compressed tar file, engManual.tar.Z, that contains sample
files as well as the main document. The Style Guide is distributed as styleGuide.ps (or .pdf).
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
PartII. Advanced Tcl
Chapter 13. Reflection and Debugging
This chapter describes commands that give you a view into the interpreter. The history command and
a simple debugger are useful during development and debugging. The info command provides a
variety of information about the internal state of the Tcl interpreter. The time command measures the
time it takes to execute a command. Tcl commands discussed are: clock, info, history, and time.
Reflection provides feedback to a script about the internal state of the interpreter. This is useful in a
variety of cases, from testing to see whether a variable exists to dumping the state of the interpreter.
The info command provides lots of different information about the interpreter.
The clock command is useful for formatting dates as well as parsing date and time values. It also
provides high-resolution timer information for precise measurements.
Interactive command history is the third topic of the chapter. The history facility can save you some
typing if you spend a lot of time entering commands interactively.
Debugging is the last topic. The old-fashioned approach of adding puts commands to your code is
often quite useful. For tough problems, however, a real debugger is invaluable. The TclPro tools from
Scriptics include a high quality debugger and static code checker. The tkinspect program is an
inspector that lets you look into the state of a Tk application. It can hook up to any Tk application
dynamically, so it proves quite useful.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter13. Reflection and Debugging
The clock Command
The clock command has facilities for getting the current time, formatting time values, and scanning
printed time strings to get an integer time value. The clock command was added in Tcl 7.5. Table 13-
1 summarizes the clock command:
Table 13-1. The clock command.
clock clicks
A system-dependent high resolution counter.
clock format value ?-format str? Formats a clock value according to str.
clock scan string ?-base clock? ?
-gmt boolean?
Parses date string and return seconds value. The clock
value determines the date.
clock seconds
Returns the current time in seconds.
The following command prints the current time:
clock format [clock seconds]
=> Sun Nov 24 14:57:04 1996
The clock seconds command returns the current time, in seconds since a starting epoch. The clock
format command formats an integer value into a date string. It takes an optional argument that
controls the format. The format strings contains % keywords that are replaced with the year, month,
day, date, hours, minutes, and seconds, in various formats. The default string is:
%a %b %d %H:%M:%S %Z %Y
Tables 13-2 and 13-3 summarize the clock formatting strings:
info nameofexecutable
The file name of the program (e.g., of tclsh or wish).
info patchlevel
The release patch level for Tcl.
info procs ?pattern? A list of all Tcl procedures, or those that match pattern.
info script
The name of the file being processed, or the empty string.
info sharedlibextension
The file name suffix of shared libraries.
info tclversion
The version number of Tcl.
info vars ?pattern? A list of all visible variables, or those matching pattern.
Variables
There are three categories of variables: local, global, and visible. Information about these categories is
returned by the locals, globals, and vars operations, respectively. The local variables include
procedure arguments as well as locally defined variables. The global variables include all variables
defined at the global scope. The visible variables include locals, plus any variables made visible via
global or upvar commands. A pattern can be specified to limit the returned list of variables to those
that match the pattern. The pattern is interpreted according to the rules of string match, which is
described on page 48:
info globals auto*
=> auto_index auto_noexec auto_path
Namespaces, which are the topic of the next chapter, partition global variables into different scopes.
You query the variables visible in a namespace with:
info vars namespace::*
Remember that a variable may not be defined yet even though a global or upvar command has
declared it visible in the current scope. Use the info exists command to test whether a variable or an
array element is defined or not. An example is shown on page 90.
Procedures
You can find out everything about a Tcl procedure with the args, body, and default operations. This
is illustrated in the following Proc_Show example. The puts commands use the -nonewline flag
because the newlines in the procedure body, if any, are retained:
Example 13-2 Printing a procedure definition.
proc Proc_Show {{namepat *}{file stdout}}{
foreach proc [info procs $namepat] {
set space ""
puts -nonewline $file "proc $proc {"
foreach arg [info args $proc] {
if [info default $proc $arg value] {
puts -nonewline $file "$space{$arg $value}"
} else {
puts -nonewline $file $space$arg
}
set space " "
}
# No newline needed because info body may return a
# value that starts with a newline
puts -nonewline $file "}{"
puts -nonewline $file [info body $proc]
puts $file "}"
}
}
Example 13-3 is a more elaborate example of procedure introspection that comes from the
direct.tcl file, which is part of the Tcl Web Server described in Chapter 18. This code is used to
map URL requests and the associated query data directly into Tcl procedure calls. This is discussed in
more detail on page 247. The Web server collects Web form data into an array called form. Example
13-3 matches up elements of the form array with procedure arguments, and it collects extra elements
into an args parameter. If a form value is missing, then the default argument value or the empty string
is used:
Example 13-3 Mapping form data onto procedure arguments.
# cmd is the name of the procedure to invoke
# form is an array containing form values
set cmdOrig $cmd
set params [info args $cmdOrig]
# Match elements of the form array to parameters
foreach arg $params {
if {![info exists form($arg)]} {
if {[info default $cmdOrig $arg value]} {
lappend cmd $value
} elseif {[string compare $arg "args"] == 0} {
set needargs yes
} else {
lappend cmd {}
}
} else {
lappend cmd $form($arg)
}
}
# If args is a parameter, then append the form data
# that does not match other parameters as extra parameters
if {[info exists needargs]} {
foreach {name value} $valuelist {
if {[lsearch $params $name] < 0} {
lappend cmd $name $value
}
}
}
# Eval the command
set code [catch $cmd result]
The info commands operation returns a list of all commands, which includes both built-in commands
defined in C and Tcl procedures. There is no operation that just returns the list of built-in commands.
Example 13-4 finds the built-in commands by removing all the procedures from the list of commands.
Example 13-4 Finding built-in commands.
proc Command_Info {{pattern *}}{
# Create a table of procedures for quick lookup
foreach p [info procs $pattern] {
set isproc($p) 1
}
# Look for command not in the procedure table
set result {}
foreach c [info commands $pattern] {
if {![info exists isproc($c)]}{
lappend result $c
}
}
return [lsort $result]
}
The Call Stack
The info level operation returns information about the Tcl evaluation stack, or call stack. The global
level is numbered zero. A procedure called from the global level is at level one in the call stack. A
procedure it calls is at level two, and so on. The info level command returns the current level
number of the stack if no level number is specified.
If a positive level number is specified (e.g., info level 3), then the command returns the procedure
name and argument values at that level in the call stack. If a negative level is specified, then it is
relative to the current call stack. Relative level -1 is the level of the current procedure's caller, and
relative level 0 is the current procedure. The following example prints the call stack. The Call_trace
procedure avoids printing information about itself by starting at one less than the current call stack
level:
Example 13-5 Getting a trace of the Tcl call stack.
proc Call_Trace {{file stdout}}{
puts $file "Tcl Call Trace"
for {set x [expr [info level]-1]}{$x > 0}{incr x -1}{
puts $file "$x: [info level $x]"
}
}
Command Evaluation
If you want to know how many Tcl commands are executed, use the info cmdcount command. This
counts all commands, not just top-level commands. The counter is never reset, so you need to sample
it before and after a test run if you want to know how many commands are executed during a test.
The info complete operation figures out whether a string is a complete Tcl command. This is useful
for command interpreters that need to wait until the user has typed in a complete Tcl command before
passing it to eval. Example 13-6 defines Command_Process that gets a line of input and builds up a
command. When the command is complete, the command is executed at the global scope.
Command_Process takes two callbacks as arguments. The inCmd is evaluated to get the line of input,
and the outCmd is evaluated to display the results. Chapter 10 describes callbacks why the curly braces
are used with eval as they are in this example:
Example 13-6 A procedure to read and evaluate commands.
proc Command_Process {inCmd outCmd}{
global command
append command(line) [eval $inCmd]
if [info complete $command(line)] {
set code [catch {uplevel #0 $command(line)}result]
eval $outCmd {$result $code}
set command(line) {}
}
}
proc Command_Read {{in stdin}}{
if [eof $in] {
if {$in != "stdin"}{
close $in
}
return {}
}
return [gets $in]
}
proc Command_Display {file result code}{
puts stdout $result
}
while {![eof stdin]}{
Command_Process {Command_Read stdin}\
{Command_Display stdout}
}
Scripts and the Library
The name of the current script file is returned with the info script command. For example, if you
use the source command to read commands from a file, then info script returns the name of that
file if it is called during execution of the commands in that script. This is true even if the info script
command is called from a procedure that is not defined in the script.
Use info script to find related files.
I often use info script to source or process files stored in the same directory as the script that is
running. A few examples are shown in Example 13-7.
Example 13-7 Using info script to find related files.
# Get the directory containing the current script.
set dir [file dirname [info script]]
# Source a file in the same directory
source [file join $dir helper.tcl]
# Add an adjacent script library directory to auto_path
# The use of ../lib with file join is cross-platform safe.
lappend auto_path [file join $dir ../lib]
The pathname of the Tcl library is stored in the tcl_library variable, and it is also returned by the
info library command. While you could put scripts into this directory, it might be better to have a
separate directory and use the script library facility described in Chapter 12. This makes it easier to
deal with new releases of Tcl and to package up your code if you want other sites to use it.
Version Numbers
Each Tcl release has a version number such as 7.4 or 8.0. This number is returned by the info
tclversion command. If you want your script to run on a variety of Tcl releases, you may need to test
the version number and take different actions in the case of incompatibilities between releases.
The Tcl release cycle starts with one or two alpha and beta releases before the final release, and there
may even be a patch release after that. The info patchlevel command returns a qualified version
number, like 8.0b1 for the first beta release of 8.0. We switched from using "p" (e.g., 8.0p2) to a three-
level scheme (e.g., 8.0.3) for patch releases. The patch level is zero for the final release (e.g., 8.2.0). In
general, you should be prepared for feature changes during the beta cycle, but there should only be bug
fixes in the patch releases. Another rule of thumb is that the Tcl script interface remains quite
compatible between releases; feature additions are upward compatible.
Execution Environment
The file name of the program being executed is returned with info nameofexecutable. This is more
precise than the name in the argv0 variable, which could be a relative name or a name found in a
command directory on your command search path. It is still possible for info nameofexecutable to
return a relative pathname if the user runs your program as ./foo, for example. The following
construct always returns the absolute pathname of the current program. If info nameofexecutable
returns an absolute pathname, then the value of the current directory is ignored. The pwd command is
described on page 115:
file join [pwd] [info nameofexecutable]
A few operations support dynamic loading of shared libraries, which are described in Chapter 44. The
info sharedlibextension returns the file name suffix of dynamic link libraries. The info loaded
command returns a list of libraries that have been loaded into an interpreter. Multiple interpreters are
described in Chapter 19.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter13. Reflection and Debugging
Cross-Platform Support
Tcl is designed so that you can write scripts that run unchanged on UNIX, Macintosh, and Windows
platforms. In practice, you may need a small amount of code that is specific to a particular platform.
You can find out information about the platform via the tcl_platform variable. This is an array with
these elements defined:
tcl_platform(platform) is one of unix, macintosh, or windows.
tcl_platform(os) identifies the operating system. Examples include MacOS, Solaris, Linux,
Win32s (Windows 3.1 with the Win32 subsystem), Windows 95, Windows NT, and SunOS.
tcl_platform(osVersion) gives the version number of the operating system.
tcl_platform(machine) identifies the hardware. Examples include ppc (Power PC), 68k
(68000 family), sparc, intel, mips, and alpha.
tcl_platform(isWrapped) indicates that the application has been wrapped up into a single
executable with TclPro Wrapper. This is not defined in normal circumstances.
tcl_platform(user) gives the login name of the current user.
tcl_platform(debug) indicates that Tcl was compiled with debugging symbols.
tcl_platform(thread) indicates that Tcl was compiled with thread support enabled.
On some platforms a hostname is defined. If available, it is returned with the info hostname
command. This command may return an empty string.
One of the most significant areas affected by cross-platform portability is the file system and the way
files are named. This topic is discussed on page 103.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter13. Reflection and Debugging
Tracing Variable Values
The trace command registers a command to be called whenever a variable is accessed, modified, or
unset. This form of the command is:
trace variable name ops command
The name is a Tcl variable name, which can be a simple variable, an array, or an array element. If a
whole array is traced, the trace is invoked when any element is used according to ops. The ops
argument is one or more of the letters r, for read traces, w, for write traces, and u, for unset traces. The
command is executed when one of these events occurs. It is invoked as:
command name1 name2 op
The name1 argument is the variable or array name. The name2 argument is the name of the array index,
or null if the trace is on a simple variable. If there is an unset trace on an entire array and the array is
unset, name2 is also null. The value of the variable is not passed to the procedure. The traced variable
is one level up the Tcl call stack. The upvar, uplevel, or global commands need to be used to make
the variable visible in the scope of command. These commands are described in more detail in Chapter
7.
A read trace is invoked before the value of the variable is returned, so if it changes the variable itself,
the new value is returned. A write trace is called after the variable is modified. The unset trace is
called after the variable is unset.
Read-Only Variables
Example 13-8 uses traces to implement a read-only variable. A variable is modified before the trace
procedure is called, so the ReadOnly variable is needed to preserve the original value. When a variable
is unset, the traces are automatically removed, so the unset trace action reestablishes the trace
explicitly. Note that the upvar alias (e.g., var) cannot be used to set up the trace:
history
Short for history info with no count.
history add command ?
exec?
Adds the command to the history list. If exec is specified, then execute
the command.
history change new ?
event?
Changes the command specified by event to new in the command
history.
history event ?event? Returns the command specified by event.
history info ?count? Returns a formatted history list of the last count commands, or of all
commands.
history keep count Limits the history to the last count commands.
history nextid
Returns the number of the next event.
history redo ?event? Repeats the specified command.
History Syntax
Some extra syntax is supported when running interactively to make the history facility more
convenient to use. Table 13-6 shows the special history syntax supported by tclsh and wish.
Table 13-6. Special history syntax.
!!
Repeats the previous command.
!n Repeats command number n.If n is negative it counts backward from the current
command. The previous command is event -1.
!prefix Repeats the last command that begins with prefix.
!pattern Repeats the last command that matches pattern.
^old^new Globally replaces old with new in the last command.
The next example shows how some of the history operations work:
Example 13-10 Interactive history usage.
% set a 5
5
% set a [expr $a+7]
12
% history
1 set a 5
2 set a [expr $a+7]
3 history
% !2
19
% !!
26
% ^7^13
39
% !h
1 set a 5
2 set a [expr $a+7]
3 history
4 set a [expr $a+7]
5 set a [expr $a+7]
6 set a [expr $a+13]
7 history
A Comparison to C Shell History Syntax
The history syntax shown in the previous example is simpler than the history syntax provided by the C
shell. Not all of the history operations are supported with special syntax. The substitutions (using
^old^new) are performed globally on the previous command. This is different from the quick-history
of the C shell. Instead, it is like the !:gs/old/new/ history command. So, for example, if the example
had included ^a^b in an attempt to set b to 39, an error would have occurred because the command
would have used b before it was defined:
set b [expr $b+7]
If you want to improve the history syntax, you will need to modify the unknown command, which is
where it is implemented. This command is discussed in more detail in Chapter 12. Here is the code
from the unknown command that implements the extra history syntax. The main limitation in
comparison with the C shell history syntax is that the ! substitutions are performed only when ! is at
the beginning of the command:
Example 13-11 Implementing special history syntax.
# Excerpts from the standard unknown command
# uplevel is used to run the command in the right context
if {$name == "!!"}{
set newcmd [history event]
} elseif {[regexp {^!(.+)$}$name dummy event]}{
set newcmd [history event $event]
} elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$}$name x old new]}{
set newcmd [history event -1]
catch {regsub -all -- $old $newcmd $new newcmd}
}
if {[info exists newcmd]}{
history change $newcmd 0
return [uplevel $newcmd]
}
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter13. Reflection and Debugging
Debugging
The rapid turnaround with Tcl coding means that it is often sufficient to add a few puts statements to
your script to gain some insight about its behavior. This solution doesn't scale too well, however. A
slight improvement is to add a Debug procedure that can have its output controlled better. You can log
the information to a file, or turn it off completely. In a Tk application, it is simple to create a text
widget to hold the contents of the log so that you can view it from the application. Here is a simple
Debug procedure. To enable it you need to set the debug(enable) variable. To have its output go to
your terminal, set debug(file) to stderr.
Example 13-12 A Debug procedure.
proc Debug { args }{
global debug
if {![info exists debug(enabled)]}{
# Default is to do nothing
return
}
puts $debug(file) [join $args " "]
}
proc DebugOn {{file {}}}{
global debug
set debug(enabled) 1
if {[string length $file] == 0}{
set debug(file) stderr
} else {
if [catch {open $file w}fileID] {
puts stderr "Cannot open $file: $fileID"
set debug(file) stderr
} else {
puts stderr "Debug info to $file"
set debug(file) $fileID
}
}
}
TclPro Compiler
TclPro Compiler is really just a reader and writer for the byte codes that the Tcl byte-code compiler
generates internally. It lets you precompile scripts and save the results, and then load the byte-code
later instead of raw source. This provides a great way to hide your source code, if that is important to
you. It turns out to save less time than you might think, however. By the time it reads the file from
disk, decodes it, and builds the necessary Tcl data structures, it is not much faster than reading a
source file and compiling it on the fly.
TclPro Wrapper
TclPro Wrapper assembles a collection of Tcl scripts, data files, and a Tcl/Tk interpreter into a single
executable file. This makes distribution of your Tcl application as easy as giving out one file. The Tcl
C library has been augmented with hooks in its file system access routines so that a wrapped
application can look inside itself for files. The rule is that if you use a relative pathname (i.e.,
lib/myfile.dat), then the wrapped application will look first inside itself for the file. If the file is not
found, or if the pathname is absolute (e.g., /usr/local/lib/myfile.dat), then Tcl looks on your
hard disk for the file. The nice thing about TclPro Wrapper is that it handles all kinds of files, not just
Tcl source files. It works by concatenating a ZIP file onto the end of a specially prepared Tcl
interpreter. TclPro comes with pre-built interpreters that include Expect, [incr Tcl], and TclX, or you
can build your own interpreter that contains custom C extensions.
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter13. Reflection and Debugging
Other Tools
The Tcl community has built many interesting and useful tools to help your Tcl development. Only
two of them are mentioned below, but you can find many more at the Scriptics Tcl Resource Center:
http://www.scriptics.com/resource/
The tkinspect Program
The tkinspect program is a Tk application that lets you look at the state of other
Tk applications. It displays procedures, variables, and the Tk widget hierarchy.
With tkinspect you can issue commands to another application in order to change
variables or test out commands. This turns out to be a very useful way to debug
Tk applications. It was written by Sam Shen and is available on the CD-ROM.
The current FTP address for this is:
ftp.neosoft.com:/pub/tcl/sorted/devel/tkinspect-5.1.6.tar.gz
The Tuba Debugger
Tuba is a debugger written purely in Tcl. It sets breakpoints by rewriting Tcl procedures to contain
extra calls to the debugger. A small amount of support code is loaded into your application
automatically, and the debugger application can set breakpoints, watch variables, and trace execution.
It was written by John Stump and is available on the CD-ROM. The current URL for this package is:
http://www.geocities.com/SiliconValley/Ridge/2549/tuba/
The bgerror Command
When a Tcl script encounters an error during background processing, such as handling file events or
during the command associated with a button, it signals the error by calling the bgerror procedure. A
default implementation displays a dialog and gives you an opportunity to view the Tcl call stack at the
point of the error. You can supply your own version of bgerror. For example, when my exmh mail
application gets an error it offers to send mail to me with a few words of explanation from the user and
a copy of the stack trace. I get interesting bug reports from all over the world!
The bgerror command is called with one argument that is the error message. The global variable
errorInfo contains the stack trace information. There is an example tkerror implementation in the
on-line sources associated with this book.
The tkerror Command
The bgerror command used to be called tkerror. When event processing shifted from Tk into Tcl
with Tcl 7.5 and Tk 4.1, the name tkerror was changed to bgerror. Backwards compatibility is
provided so that if tkerror is defined, then tkerror is called instead of bgerror. I have run into
problems with the compatibility setup and have found it more reliable to update my applications to use
bgerror instead of tkerror. If you have an application that runs under either Tk 4.0 or Tk 4.1, you
can simply define both:
proc bgerror [info args tkerror] [info body tkerror]
Top
Practical Programming in Tcl & Tk, Third Edition
By BrentB.Welch
Table of Contents
Chapter13. Reflection and Debugging
Performance Tuning
The time command measures the execution time of a Tcl command. It takes an optional parameter
that is a repetition count:
time {set a "Hello, World!"}1000
=> 28 microseconds per iteration
If you need the result of the command being timed, use set to capture the result:
puts $log "command: [time {set result [command]}]"
Time stamps in a Log
Another way to gain insight into the performance of your script is to generate log records that contain
time stamps. The clock seconds value is too coarse, but you can couple it with the clock clicks
value to get higher resolution measurements. Use the code shown in Example 13-1 on page 175 to
calibrate the clicks per second on your system. Example 13-13 writes log records that contain the
current time and the number of clicks since the last record. There will be occasional glitches in the
clicks value when the system counter wraps around or is reset by the system clock, but it will normally
give pretty accurate results. The Log procedure adds overhead, too, so you should take several
measurements in a tight loop to see how long each Log call takes:
Example 13-13 Time Stamps in log records.
proc Log {args}{
global log
if [info exists log(file)] {
set now [clock clicks]
puts $log(file) [format "%s (%d)\t%s" \
[clock format [clock seconds]] \
Practical Programming in