Professional Documents
Culture Documents
Each Tcl command call is a sentence of the form : command arg1 arg2 arg3 ...
The Tcl evaluator take each word of this sentence and evaluate it. After evaluation of each
word, the first word (command) is considered to be a function name and this function is
executed with as arguments the following words.
To evaluate a word, the interpretor has to do the following substitutions in the word string :
If the word is surrounded by " ", this word may contain spaces, but substitution is still
applicable inside the quotations. Inside the quotation, there may be spaces and
carriage returns.
If a word is surrounded by { }, this word is unaffected (substitution is thus not
applicable on this word). Inside the braces, there may be spaces and carriage returns.
Moreover, the { } braces may be nested.
where substitution is applicable, every string beginning with $ is replaced with the
variable represented by this string. This string is ended by a space, a '-' or a ','.
Examples
1. set a "World !"
In the evaluation of the 3 words 'set', 'a' and '"World !"', no substitution has to be done,
only the " " are removed. The command 'set' is then executed with as parameters 'a'
and 'World !'. This command tell Tcl to define a new variable 'a' (if not already
defined) and to set its value to 'World !'.
2. set b "Hello $a"
Set the variable 'b' to 'Hello World !'. Here, the variable substitution has occurred
inside the second parameter where the variable 'a' is replaced by its value.
3. set c [string range $b 0 3]
Set the variable c to 'Hell', which is the 4 first letters of 'Hello World !'. In this case,
The part between [ ] has been executed as a command
If you want to break a command sentence in lines you can only do it inside the { } brace or in
the " " quotation or you can break the line with a '\' at the end of any break line.
Example
if {$c == "Hell"} {
puts "Oh god !"
} else {
puts "Peace !"
}
This test the value of the variable c. If it is the string 'Hell' it prints 'Oh god !' on screen,
otherwise, it prints 'Peace !'. In this sentence, Tcl see 5 words :
'puts "Oh god !"' : for the same reason, no further evaluation
The first word, 'if' is seen as the command and this command is executed with as parameters
the 4 following words. That is later that the condition '$c == "Hell"' is evaluated, during the
execution of the if command.
Notice where we placed the line breaks (inside the { }).
Mathematics expression
Whereas all variables are of type string, the mathematical operations internally uses float and
integer number representation to produce their results. The command that calculate
mathematical expression is 'expr'.
Example
% set result [expr (4+6)/4]
2
% set result [expr (4.0+6)/4]
2.5
In the first calculation, the interpretor has used the integer number representation. In the
second, it has used the float number representation.
As it can be seen in the previous example, the command format is very similar to the C
command 'printf'.
Control flow
The following commands are similar to the C equivalent. Only 'foreach' has no C equivalent
(have a look at the example to see what it do).
if {...condition...} {...body...}
while {...condition...} {body}
the '...condition...' is evaluated in the same way that it should be with command 'expr'.
Examples
1. while
2.
3. % while {$i<4} {
4. > puts "$i*$i is [expr $i*$i]"
5. > incr i
6. > }
7. 0*0 is 0
8. 1*1 is 1
9. 2*2 is 4
10. 3*3 is 9
11. for
12.
13. % for {set i 0} {$i<4} {incr i} {
14. > puts "$i*$i is [expr $i*$i]"
15.
16.
17.
18.
19.
> }
0*0
1*1
2*2
3*3
is
is
is
is
0
1
4
9
20. foreach
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
% set observations \
{Bruxelles 15 22 London 12 19 Paris 18 27}
Bruxelles 15 22 London 12 19 Paris 18 27
% foreach {town Tmin Tmax} $observations {
> set Tavg [expr ($Tmin+$Tmax)/2.0]
> puts "$town $Tavg"
> }
Bruxelles 18.5
London 15.5
Paris 22.5
Array
Arrays are always unidimensional but the index is a string. If you use a separator in the index
string (such as ',', '-'), you can get the same effect than with a multidimensional array in other
languages.
Example
% set observations \
{Bruxelles 15 22 London 12 19 Paris 18 27}
Bruxelles 15 22 London 12 19 Paris 18 27
% foreach {town Tmin Tmax} $observations {
set obs($town-min) $Tmin
set obs($town-max) $Tmax
}
% parray obs
obs(Bruxelles-max) = 22
obs(Bruxelles-min) = 15
obs(London-max)
= 19
obs(London-min)
= 12
obs(Paris-max)
= 27
obs(Paris-min)
= 18
Procedures
Procedures are the equivalent of the C functions.
Example
% proc sum2 {a b} {
> return [expr $a+$b]
> }
if a procedure does not contain any 'return' statement, the default return value is the return
value of the last evaluated function in this procedure. So the following script is perfectly
equivalent :
% proc sum2 {a b} {
>
expr $a + $b
> }
The special argument name 'args' contains a list with the rest of the arguments
Example
% proc sum {args} {
>
set result 0
>
foreach n $args {
>
set result [expr $result+$n]
>
}
>
return $result
> }
% sum 12 9 6 4
31
it is also possible to specify default parameters. So, if you don't specify the last parameters,
the default values will be substituted.
Example
%
>
>
>
>
%
1
2
3
%
1
3
5
count 1 5 2
If you want to use global variables in a function, you have to declare it as global.
Example
% set global_counter 3
% proc incr_counter {} {
>
global global_counter
>
incr global_counter
> }
% incr_counter
4
% set global_counter
4
set counter(value) 3
set counter(active) 1
proc incr_counter {} {
global counter
if {$counter(active)} {
incr counter(value)
}
}
incr_counter
set counter(active) 0
incr_counter
Eval
The 'eval' command
In the following example, we used the function 'sum' that we have already defined.
Example
% proc average {args} {
>
return [expr [eval sum $args] / [llength $args]]
> }
% average 45.0 65.0 78.0 55.0
60.75
If you had omitted the 'eval' command in the previous example, the 'sum' procedure would
have returned an error because 'sum' should be called with only one string argument (in the
previous example, this argument would have been '45.0 65.0 78.0 55.0') while 'sum' is
expecting numerical arguments.
uplevel, upvar
With the 'upvar' command, you can access a variable which belongs to a higher level of the
procedure call stack.
Example
% proc decr {n steps} {
>
upvar $n upa
>
set upa [expr $upa - $steps]
> }
% set nb 12
12
% decr nb 3
9
% puts $nb
9
In the previous example, the parameter 'n' gets the value 'nb' (the string 'nb' !) if we type 'decr
nb 3'. The command 'upvar $n upa' means that the variable 'upa' becomes a synonym to the
variable 'nb' (coming from a higher level of the stack).
With the 'uplevel' command, you can evaluate something on higher level in the stack.
Example
% proc do {todo condition} {
>
set ok 1
>
while {$ok} {
>
uplevel $todo
>
if {[uplevel "expr $condition"]==0} {set ok 0}
>
}
> }
% set i 0
0
% do {
puts $i
incr i
} {$i<4}
0
1
2
3
Inside the procedure 'do', the evaluation of the script 'todo' and the conditional 'condition' has
to made on a higher level of stack (in the same way that if they were evaluated from out of
'do').
If 'catch' return 0, it means that no error occurred while evaluating the script send as
parameter of catch. If 'catch' return 1, it means that an error occurred.
Example
% proc div {a b} {
>
if {$b==0} {
>
error "divided by zero"
>
} else {
>
return [expr $a/$b]
>
}
> }
% div 8 3
2
% div 8 0
divide by zero
% catch {div 8 3}
0
% catch {div 8 0}
1
% catch {set result [div 8 3]}
0
% catch {set result [div 8 0]}
1
Introduction
Emacs is a powerful text editor. Especially suited for
programming, it uses intensively the keyboard shortcuts. This
page is a short summary of the most used shortcuts in Emacs. If
you are configuring your Linux, you will appreciate that Emacs
also works in text mode.
File access
Ctrl-x Ctrl-f
Open file
Ctrl-x Ctrl-s
Save
Buffer control
Ctrl-x k
Kill buffer (close file)
Ctrl-x b
Switch to an other buffer
Ctrl-x 2
Split the window vertically to see an extra buffer
Ctrl-x 3
Split the window horizontally to see an extra buffer
Ctrl-x 0
Hide the current window
Ctrl-x 1
Show only the current window
Ctrl-x o
Jump to an other window
Text editing
Ctrl-Space
Marks beginning of a block
Alt-w
Marks end of block and copy
Ctrl-w
Marks end of block and cut
Ctrl-y
Paste
Ctrl-a
Go to the beginning of the line
Ctrl-e
Go to the end of the line
Ctrl-k
Kill end of line (after the cursor)
General
Ctrl-g
Cancel the command you are typing
Ctrl-x Ctrl-c
Quit emacs
Ctrl-_
Undo
TAB
Automatic indentation (source code) while editing
TAB
Automatic completion while selecting a file or while typing a
command in the mini-buffer
Alt-x
Type a command in the mini-buffer
Alt-x c-mode
Change major mode to c-mode
Alt-x global-font-lock-mode
Files comparison
Alt-x ediff
Compares two files
Spell check
Alt-x ispell-buffer
Spell check for the current buffer
Help
Ctrl-h ?
General help functions
Ctrl-h f
Describe the command you type
Ctrl-h a
apropos ... help based on a keyword
Ctrl-h m
Help on the current major mode
Return to introduction
Suppose that you have only two users on your local PC, let's say Paul Dupont and
Luc Jackson. The former has the login 'paul' and the last has the the login 'luc'.
Through your local Internet provider, you have access to a POP3 mailbox. Your
address for this mailbox is 'dfzt12@provider.com'. Luc and Paul have subscribed
an alias address, respectively Luc.Jackson@advalvas.be and
Paul.Dupont@advalvas.be. These aliases forward any received mail to your
mailbox 'dfzt12@provider.com'.
On the local PC, Luc and Paul have respectively the local addresses
'luc@linuxpc.localdomain' and 'paul@linuxpc.localdomain' where 'linuxpc' is the
name you have chosen for your computer. Luc and Paul wish to read their mail
with Netscape, through a local POP3 server. As they send a mail to an Internet
address, they wish to be able to send this mail off-line. They also wish that the
address that the recipient will see doesn't contain the name of your local computer
(linuxpc) but well the name of the alias server domain (advalvas.be). So, if luc
send a mail to 'somebody@other.host', 'somebody' will think that the mail come
from 'luc@advalvas.be'.
This situation is illustrated in the following figure.
3. Sendmail configuration
3.1 /etc/sendmail.cf
To build the sendmail configuration file, it is highly suggested to use the m4 tool.
In the directory /usr/lib/sendmail-cf/cf, create a file .mc. For example, my
file /usr/lib/sendmail-cf/cf/linuxpc.mc contains the following :
divert(-1)
# This is the macro config file used to
generate the /etc/sendmail.cf
Begin of a comment
text
include(`../m4/cf.m4')
Include a standard
macro.
OSTYPE(`linux')dnl
Include an OS-specific
macro.
undefine(`UUCP_RELAY')
undefine(`BITNET_RELAY')
define(`confAUTO_REBUILD')
Automatically rebuild
alias file if needed.
define(`confTO_CONNECT', `1m')
Maximum time to
connect ???
define(`PROCMAIL_MAILER_PATH',`/usr/bin/pro
cmail')
define(`confTO_QUEUEWARN', `48h')
dnl
define(`SMART_HOST',`relay.provider.com')
FEATURE(use_cw_file)
FEATURE(local_procmail)
FEATURE(`genericstable',`hash -o
/etc/mail/genericstable.db')
FEATURE(masquerade_envelope)
Define a 'generic'
domain.
GENERICS_DOMAIN(`linuxpc.localdomain')
Define a 'generic'
domain.
GENERICS_DOMAIN(`localhost')
Define a 'generic'
domain.
GENERICS_DOMAIN(`localhost.localdomain')
Define a 'generic'
domain.
MASQUERADE_AS(`advalvas.be')
MASQUERADE_DOMAIN(`linuxpc.localdomain')
MAILER(procmail)dnl
MAILER(smtp)dnl
In order to create the database file /etc/mail/genericstable.db, one should type the
following :
makemap hash /etc/mail/genericstable.db < /etc/mail/genericstable
3.3 /etc/aliases
Optionally, you may change the alias file. In this way, you could, for example, send
a mail to 'paul.dupont' locally (without domain name) and this mail would be
directly delivered to 'paul'. So, add the following line on the end of /etc/aliases
Paul.Dupont: paul
Luc.Jackson: luc
After doing this, you have to rebuild the alias database. Type
newaliases
3.4 /etc/sendmail.cw
This is the file containing names of hosts for which we receive email. In our
example,
linuxpc
localhost
localhost.localdomain
linuxpc.localdomain
If you want that every user is able to send the mail queue, you have to set the user
ID on execution.
chmod a+s /usr/sbin/sendmail
The POP2 and POP3 server daemon are name ipop2d and ipop3d, respectively. In
the Redhat distribution, they are included in the package imap. You can check if
this package is installed :
rpm -q imap
If not, look for a file named imap*.rpm on the installation CDROM and install it
with
rpm -ivh imap*.rpm
4.2 /etc/inetd.conf
the inetd daemon is responsible for listening on the TCP/IP port and to start the
appropriate daemon (dependent on the port number) when a connection is
established. For example, the pop-3 service use the port 110. Every time one try to
connect to the pop-3 service (on port 110), inetd will start ipop3d.
In /etc/inetd.conf, be sure that the following lines are presents and uncommented :
pop-2 stream tcp nowait root /usr/sbin/tcpd ipop2d
pop-3 stream tcp nowait root /usr/sbin/tcpd ipop3d
The files /etc/hosts.allow and /etc/hosts.deny describe which hosts are allowed to
use the local INET services. If you want that only the local host be able to access
this service, set the following :
In /etc/hosts.allow,
ALL: LOCAL
In /etc/hosts.deny,
ALL: ALL
4.4 /etc/services
You have to make sure that the services pop-2 and pop-3 are defined. Check
in /etc/services that the following lines are presents :
pop-2 109/tcp postoffice
pop-3 110/tcp
It make the association between a service name (i.e. pop-3) and a service number
(i.e. 110).
4.5 restart inetd
5. Configuration of fetchmail
fetchmail fetches the mail on a POP3 server and redistribute the mail locally
via sendmail. Each user (who must be able to fetch the mail for everybody) must
have a configuration file .fetchmailrc in his home directory. In our example, it
looks like this :
poll pophost.provider.com with protocol POP3 aka advalvas.be no dns :
user dfzt12 fetchall with password kdwMlp45 to Paul.Dupont=paul
Luc.Jackson=luc
The 'aka' is only used for multi-drop. In this example, it declares a DNS alias for
'provider.com'. If you look at the fetchmail man page, you will read :
When fetchmail, while processing a multi-drop mailbox, grovels through message
headers looking for names of the mailserver, pre-declaring common ones can save
it from having to do DNS lookups.
The option 'no dns' prevents the multi-drop system from looking for domain name
aliases (for each recipient). If this option wouldn't be specified, it should be
possible that a mail cannot be fetched because one of the recipient has an invalid
domain name.
The option 'fetchall' ensures that all messages are fetched even the message that
you have already read.
5.2 Check the permission of .fetchmailrc
fetchmail won't work if the configuration file hasn't the appropriate permission.
chmod 0710 .fetchmailrc
Class outline:
Tcl
Background
Variables Set, Unset
Comments
Basic Input/Output
Procedures
Upvar
Arithmatic Operations
String Operations
List Operations
Control Flow
Global variables
Array Operations
More Input/Output
File Operations
System Operations
Background
Tcl is an intepreted high-level programming language designed to be easily
understandable and easily customizable. It was developed by John Ousterhout
when he wanted to give his students an editor that they could alter and extend
themselves. One of tcls nicest features is that its very easy to write and very easy
to read. Tcl code is simple and straightforward, for the most part, and as such is
easy to modify.
Tk is an associated graphical toolkit which provides the same power to users in the
graphical domain. Now complex GUIs can be written in a few dozen lines of code,
rather than the pages it requires in more complicated arenas.
set curr {}
set temp [gets $f]
while {![eof $f]} {
if {$temp=="---"} {
set thelist [lappend thelist
$curr]
set curr {}
} else {
set curr [lappend curr $temp]
}
set temp [gets $f]
}
set thelist [concat $thelist [list $curr]]
return $thelist
}
proc cleanNum {num} {
## this procedure strips off leading zeros
if {[string index $num 0]!="0"} {
return $num
} else {
return [cleanNum [string range $num 1
end]]
}
}
foreach el $bit {
if {$result=={}} {
set result $el
} else {
set result "[set
result]\n[set el]"
}
}
return $result
}
proc driver {} {
global argv argc
if {$argc==0} {
puts stdout "Format is z <name> \
[other stuff\]"
return
}
set siglist [getsigs ~/.zephyr.list.short]
set sig [pickone $siglist]
puts stdout $sig
puts stdout "Type your message now. End with
control-D or a dot on a line by itself."
if {[llength $argv]>4} {
return
Outline
This course will cover Tcl and Tk at a basic level, and looking at features of the
language one by one along with examples of the code at work. In the end, well see
that a basic word processing program can be written very simply using Tcl and Tk.
Well close by briefly looking at a number of advanced topics which are beyond
the scope of this class, but that you can learn about on your own if you like.
In the first class, well concentrate on Tcl, and in the second, well look at how Tk
can extend Tcl to allow the creation of graphical interfaces very simply.
Writing Conventions
Text in Courier font
set a [expr $b * 12]
represents code exactly as typed into the interpreter.
Text in italics represents an abstract thing to be filled in. This text
set f [open filename r]
means that the command takes a filename in that space. (i.e.
set f [open "foo.txt" r]
Text wrapped in ? ? means that it is optional.
puts ?nonewline? textstring
Finally, means that there are multiple things there.
foo arg arg
add tcl
tclsh
Tk Interpreter
This will bring up the Tcl interpreter in the window you run it in, but this tcl
interpreter can run tk commands as well. A Tk window will also be created.
add tcl
wish
To run a file
To execute a file of tcl or tk code, there are two ways to do it. One is to make the
file executable:
chmod 700 filename
and to put
#!/mit/tcl/bin/tclsh
or #!/mit/tcl/bin/wish
at the start of the file.
Zephyr instance
I encourage all of you to work together as youre learning Tcl and Tk, and a zephyr
instance is a good way to do that. Ill be subbed to this whenever I can, and you
can ask your questions about Tcl and Tk on the instance where everyone can see
them and benefit from the answers.
To subscribe for this login session only:
zctl sub message tcltk \*
To unsubscribe for this login session only:
zctl unsub message tcltk \*
To subscribe forever:
zctl add message tcltk \*
To unsubscribe forever:
zctl delete message tcltk \*
To send a message
zwrite i tcltk
To read the logs of the instance
add zlog
cd /mit/zlog
more tcltk (or emacs tcltk, or whatever)
Tcl
Variables Set, Unset
The core of the tcl language is variables. Unlike other languages, every variable in
tcl is a string. The string "123" is the number one hundred twenty-three, but it is
also the character string made of the characters 1,2 and 3. It depends on the
context in which the variable appears.
Assigning a variable a value is done with the set command
Basic Input/Output
Input and output in tcl is pretty simple. Puts and gets are the output and input
commands.
set a [gets stdin] <- this reads in a value from the console and stores it in
a
puts stdout $a <- this command echos back the value to the console
puts stdout "Hello world"
puts stdout "This is value a: $a"
set b "User says [gets stdin]
The default location for puts is stdout, but gets has no default location. Go figure.
puts "Hello world"
By default, puts puts a carriage return at the end of the string it prints, but you can
disable this with the nonewline flag.
puts nonewline "These are on "
puts "the same line."
Procedures
You can define procedures in tcl and call them.
proc foo {} {
puts "This is procedure foo"
}
A procedure can take arguments.
proc bar {x y z} {
puts "$x $y $z"
}
A procedure can return a single value.
proc baz {x y} {
return [expr $x + $y]
}
set a [baz 2 3]
Procedure arguments can have default values
proc foo2 {{x 12} {y 3}} {
return [expr $x + $y]
}
set b [foo2] <- b is assigned 12 + 3 = 15
set c [foo2 7] <- c is assigned 7 + 3 = 10
set d [foo2 1 8]<- d is assigned 1 + 8 = 9
Note that the arguments with default values must be at the end of the list of
arguments, and no arguments with default values may precede arguments without
default values.
Procedures can even take any number of parameters, if the last parameter is named
"args". Here, calling this procedure with any number of arguments will pass in the
arguments as a list (more on lists later) bound to the identifier args.
proc bar2 {args} {
puts "Arguments are $args"
}
An Example
This somewhat contrived example takes in values from the console and echos them
back.
proc foo {a b c} {
puts stdout "First input was $a"
puts stdout "Second input was $b"
puts stdout "Third input was $c"
}
puts stdout "Input 1:"
set in1 [gets stdin]
puts stdout "Input 2:"
set in2 [gets stdin]
puts stdout "Input 3:"
set in3 [gets stdin]
foo $in1 $in2 $in3
Upvar
In the arguments we have seen before, arguments are passed "by value". This
means that in the code
proc foo {a b c} {
puts "$a $b $c"
}
set f 11
set g 22
set h 33
foo $f $g $h
The interpreter first computes the value of variable f (11), the value of variable g
(22), and the value of variable h (33), and then send them (11,22,33) to the
procedure foo. Foo knows nothing about where those values came from, and from
foos perspective, the call might well have been
foo 11 22 33
The other method of passing arguments is called "call by name". In this calling
convention, a function can access and modify variables from the caller. The upvar
command works to allow you to do this.
Upvar takes in the name of a variable from the caller and the name of a variable to
"bind" that variable to. After the upvar command, any reference to the second
variable actually refers to the named variable in the caller. To pass a variable by
name, you send in the name of the variable, rather than its value. For example:
proc foo {name1 name2 name 3} {
upvar $name1 a
upvar $name2 b
upvar $name3 c
puts "$a $b $c"
}
set f 11
set g 22
set h 33
foo "f" "g" "h"
Now, the caller calls foo with the names of its three variables (f, g, and h). Foo uses
upvar to say that after the 3 upvar commands, any reference to "a" really means a
reference to the variable "f" as defined in the scope of the caller. Now, in the puts
command when the value of a is printed out, the value of f in the caller is really the
thing that gets printed.
Upvar can also allow you to modify variables in the caller.
proc foo {name} {
upvar $name a
set a [expr $a + 1]
}
set f 11
puts $f <- f is 11 here
foo "f"
puts $f <- f is 12 here
Comments
Comments are text within a program that is ignored when running the program, but
used to leave reminders and notes for other people who read the source code later.
Comments in Tcl are specified by the # character at the beginning of the line. This
comments all the text to the end of the line. Note that you can only use # where the
Tcl interpreter expects a Tcl expression, so you are somewhat restricted in where
you can use it. In general, comments should be on their own lines to be safe.
# this procedure does something
proc foo {} {
puts "Hello world"
# is this correct?
puts "Hello werld"
}
Arithmatic Operations
The simplest arithmatic operations are simply incrementing and decrementing an
integer. This is done with the incr and decr commands, which take in the name of
the variable to increment. Note that this is a call by name command, so you send in
the variable name, not the value of the variable.
(i.e. incr a, not incr $a)
set a 3
incr a <- a is now 4
decr a <- a is now 3 again
Arithmatic operations in Tcl are executed with the expr command
set a [expr 2 + 3]
set b [expr (2 + 3) * 4]
A number of more complex operations can be done with expr as well:
set c [expr 7 << 3] <- c is assigned 7 left shifted by 3, i.e. 7 * 23 = 56
set d [expr 7 >> 3] <- d is assigned 7 right shifted by 3, i.e. 7 / 23
Note that the "type" of the input determines the operation in some cases
set e [expr 7 / 3] <- e is assigned 7 integer-divided by 3, or 2
set f [expr 7.0 / 3.0] <- f is assigned 7 float-divided by 3, or 2.3333
Other math operations have a slightly different notation
set g [expr pow(3,2)] <- g is assigned 3 raised to the second power
set h [expr acos(.8)] <- h is assigned the arc-cosine of .8
Tcl supports many math operations:
acos(x)
asin(x)
atan(x)
Another example
This procedures takes in inputs and adds them up until they sum to more than 100.
#running total takes in the name of the total counter
#and the value to add to it
proc runningtotal {totalname new} {
upvar $totalname current
set current [expr $current + $new]
}
set total 0
while {$total < 100} {
puts stdout "Total is now $total."
puts stdout "Input new value:"
set inputvalue [gets stdin]
runningtotal "total" $inputvalue
}
puts stdout "Done."
String Operations
Since all Tcl values are stores as strings, it makes sense to have a number of string
operations that can be done. Just as expr preceded arithmatic expressions, string
precedes these commands.
set a [string length "Hello world"] <- a gets the string length of
the string, which is 11
set b [string index "Hello world" 3] <- b gets the 3rd character in
Hello World
Note that strings are 0-indexed, so the 0 th character is the first one in the string.
Other string operations:
string compare str1 str2 Returns 0 if equal, -1 if str1 sorts before str2
lexographically, 1 if str2 sorts before str1
string first sub str Returns the index of the first character of the first
occurrence of substring sub appearing in
str
string last sub str Returns the index of the first character of the last
occurrence of substring sub appearing in
str
string match pattern str Returns 1 if str matches the pattern in pattern.
Uses glob-style matching. (* and ?
wildcards)
string range str I j Returns the range of characters from index i to index j.
string tolower str
string toupper str
string trim str ?chars? Trims the characters in the string chars from the front and
end of str. chars defaults to whitespace if it is not present.
string trimleft str ?chars?
string trimright str ?chars?
string wordend str ix Return the index in str of the character after the word
containing the character at ix.
string wordstart str ix Return the index in str of the first character in the word
containing the character at ix.
Append
Another useful string command is append. This appends one or more arguments to
a variable. Note that the first argument is the NAME of the variable, not [set
variable] or $variable.
set a "abc"
set b " some letters"
append a " is " $b "that I like"
puts $a <- this will print "abc is some lettersthat I like"
Note: append is efficient, much more so than doing something like
set x "abc "
set x "$x some letters that I like"
Format
Format is like the C printf function.
set a [format "%d %f %s" 10 4.3 "hello"] <- a is assigned "10
4.3 hello"
Scan
Scan is like the C scanf function.
scan "10 4.3 hello" "%d %f %s" x y z <- assigns x to be 10,
assigns y to be 4.3, assigns z to be hello
List Operations
The most common data structure in Tcl is a list.
Lists can be created in a number of ways.
set a [list "a" "b" c"]
<- a is assigned to be a list with three elements, "a", "b", and "c"
Curly braces {} can be used as shorthand for the list command.
set a {"a" "b" "c"}
<- a is assigned to be the same list as earlier
Lists can be nested:
set a [list "a" [list "b" "c"] "d"]
Control Flow
Control flow in Tcl works like in many other languages.
continue, break
The continue and break commands allow control to jump out from the inside of a
loop. Break breaks out of the loop and runs the next command, and continue jumps
back to the start of the loop.
In some sense, break stops the loop totally and continue skips to the next run
through the loop.
switch
Switch allows control to jump to a number of expressions depending on what a
value is.
switch -- $name {
catch
Tcl supports error handling. If you use the catch command, you can try execute a
block of Tcl expressions and trap any errors that result.
error
The error command is the counterpart to catch. It signals an error that will trigger
an enclosing catch or halt the program with an error message if there is no
enclosing catch.
error message
return
The return command returns from a procedure, returning a value.
return string
set months {}
foreach el {"January" "February" "March"
"April" "May" "June" "July"} {
lappend months $el
}
return $months
}
set data {}
foreach el [makelist] {
puts $el
set data [concat $data $el]
if {[llength $data] > 3} {
break
}
}
puts "done"
Global variables
So far weve looked only at local variables, variables in the same procedure where
theyre used. Variables can also be global, and thus accessible anywhere in the
program. Global variables are somewhat dangerous to use, but theyre really quite
useful, especially in small programs that are easily understood.
To declare a variable global, you simply use the global command
proc setGlobals {val1 val2 val3} {
global a b c
set a $val1
set b $val2
set c $val3
}
proc printGlobals {} {
global a b c
puts "$a $b $c"
}
If you declare a variable outside of any procedures, it is implicitly global.
set a 12
proc foo {} {
global a
puts $a
}
Array Operations
Another construct Tcl provides is an array indexed by string values. This is
essentially a hash table, and allows efficient association of keys and values.
To create an array, simply set a variable along with an index.
set myArray("redKey") "redValue" <- this will create an array called
myArray if it
doesnt exist already, and add
the value
"redValue"
associated with
the key
"redKey"
To get the value out again, just use set like we did before.
set theValue [set myArray("redKey")]
or
set theValue $myArray("redKey")
You can test whether a key is present in an array by using info exists, just like for
regular variables.
if {[info exists myArray("redKey")]} {
puts $myArray("redKey")
}
The array command allows a number of operations on arrays as well:
array exists arr <- returns 1 is arr is the name of an array variable
array get arr ?pattern? <- turns an array into a list of alternating keys
and values
pattern selects the entries to put into the list, if present
(beats the hell out of me how the pattern works, though)
array names arr ?pattern? <- returns a list of the names of the keys in
the array
applies the pattern to filter, if present
array set arr list <- create arr from the data in list, which is in the same
form
as returned from get
array size arr <- return the number of keys in the array arr
You can iterate through an array as well. Look at the documentation for
information on startsearch, nextelement, anymore, and donesearch.
Note that passing arrays between procedures requires call by name, using upvar.
More Input/Output
File input/output is reasonably straightforward.
open filename ?access? ?permissions?
access can be:
r Open for reading, file must exist
r+ Open for reading and writing, file must exist
w Open for writing, replace if exists, create if does not
w+ Open for reading and writing, replace or create as needed
a Open for writing, data is appended to file
a+ Open for reading and writing, data is appended
Permissions are the standard numbers used in chmod, but you need a leading 0 to
get an octal number.
set fileId [open /tmp/foo w 0600]
Once open, a file can be written to
puts $fileId "Hello world"
or read from
set fileId2 [open /tmp/foo r]
set in [gets $fileId2]
To close an open file (you must close a file before the Tcl program ends to store
your changes), simply use close on the fileID
close $fileId
You can also pass nonewline to puts, if you dont want to put an end-of-line after
the string.
puts nonewline $fileId "Hello world"
File Operations
Tcl provides a number of operators on files that can be quite useful.
file atime name <- returns file access time as a decimal string
file attributes name ?option? ?value? <- query or set file attributes
file copy ?-force? source destination <- copy files or directories
file delete ?-force? name
file dirname name <- return parent directory of file name
file executable name <- return 1 if executable, else 0
file extension name <- return the extension (.txt) of the file
file isdirectory name <- returns 1 or 0
file isfile name <- return 1 if name is not a directory, symlink, or device, else 0
file join path path <- join pathname components into a single pathname
file lstat name var <- place attributes of the link name into var
file mkdir name
file nativename name <- return the platform-native version of name
file owned name <- return 1 if current user owns the file name, else 0
file pathtype name <- relative, absolute, or driverelative
file readable name <- return 1 if readable, else 0
file readlink name <- return the contents of symlink name
file rename ?-force? old new
file rootname name <- return all but the extension in name (i.e. strip off .txt or
whatever)
System Operations
Tcl provides the functionality to execute a number of system calls directly.
exec
The exec program allows you to execute programs from your Tcl script. The
standard output of the program will be returned.
set d [exec date]
If the program writes to the standard error channel or exits with a non-zero exit
code, youll need to use catch to get the information you want.
catch {exec program arg arg} result
Exec has a lot of weird specifics about how it works, so look at the documentation
for specifics.
http://www.scriptics.com/man/tcl8.0/TclCmd/exec.htm
exit
The exit command terminates your Tcl program. An integer argument to exit sets
the exit status of the process
exit
exit 1
pid
The pid command returns the process ID of the current process.
set thisID [pid]
environment variables
You can get access to environment variables with the global variable env. This loop
prints out all the environment variables and their values.
global env
foreach el [array names env] {
puts "$el - $env($el)"
}
Links of interest:
The Tcl Platform Company
http://www.scriptics.com/
Brent Welchs book (excellent)
http://www.beedub.com/book/
Tcl/Tk Man pages
http://www.scriptics.com/man/tcl8.0/contents.htm
Tcl/Tk Plugin for Netscape, Internet Explorer
http://www.scriptics.com:8123/plugin/