In 2001, I moved much of the material here to a variety of individual pages within the Tcl-ers' Wiki. Now, for the most part, I update the Wiki pages specific to a particular construct, and this FMM page, which aimed to span the confusions of a breadth of different commands, is mostly dormant. If you find this page helpful, I strongly urge you to give that resource--the Wiki--a bit of your attention.
In 2007, I continue to maintain this rather passively: updating it whenever someone raises a point, and occasionally adding new material, but mostly relying on the Wiki to accumulate community wisdom.
#!/usr/local/bin/wishor
#!/usr/local/bin/wish8.0or
#!/bin/sh? Maybe none of the above. In particular, although the latter is what the distribution documentation gives, it's been known for a long time that this is in error. Tom Tromey explains why. [Explain env, ...]
# the next line restarts using wish \
exec wish "$0" "$@"
bind . <Return> {.l configure -text [format "Click for %6.2f" $number]}when what's really wanted is
bind . <Return> {.l configure -text [format "Click for %%6.2f" $number]}
I often hear that from people who haven't yet learned to ask tclsh to interpretwho | awk '{ print $1 }'
works from the command line, but my Tcl interpreter rejectsexec who | awk '{ print $1 }'
exec who | awk {{ print $1 }}
Notice that the '-s aren't "about" awk; they're just conventional
quoting
in the shells from which one most often accesses awk.
exec doesn't use the shells.
{} perform an analogous function of quoting for Tcl.
More examples appear in
this
Google thread.
Alexandre Ferrieux gives a
correct
and concise explanation
in another thread.
The problem at hand for him happened to do with
sed
, but that's essentially
inconsequential; the command-line syntaxes of awk
and sed
exhibit identical symptoms.
Briefly, as the error messages which arise themselves say,
the single quotes (') are the problem. These
are shell markup for strings which shall not be substituted.
/bin/sh
and
others remove this markup before calling awk.
In Tcl, in contrast, braces {} serve the same purpose.
Braces have the advantage that they can be nested.
One conclusion: brace the braces that awk
expects:
awk -F " " {{print $1}}
.
Note that, in this last, it is not necessary to escape
the dollar-sign, because the outer brace protect it,
too.
Much the same has been written several times by Richard Suchenwirth, Mark Janssen, Kevin Kenny, and others; it's unlikely any particular expression was invented in isolation.
radiobutton .a -text a -variable variable -value a radiobutton .b -text b -variable variable -value b pack .a .b bind .a <ButtonRelease-1> procedure bind .b <ButtonRelease-1> procedure proc procedure {} {global variable; puts $variable} set variable blahillustrates. It typically surprises those who run this script to see that the first line which appears on selecting a button reads, "blah". Results become happier when the lines
bindtags .a {Radiobutton .all .a} bindtags .b {Radiobutton .all .b}are added, as Bryan Oakley explains.
Remember the syntax: "bind tag sequence script". It's easy to forget one of the middle elements.
global i for {set i 0} {$i < $limit} {incr i} { button .button$i -command {puts "This is button #$i."} ... }but whichever button I push, it looks like the last button." I'll eventually explain the matter in this space; meanwhile, try
button .button$i -command "puts \"This is button #$i.\""
It also operates the other way; someone writes
button .mybutton -command "puts $my_updated_global"and is disappointed when button pushes seem to reset my_updated_global. He or she will, of course, be happier with
button .mybutton -command {puts $my_updated_global}
exec cdTcl has a built-in command cd, and that's much more likely to be what you want.
set a value puts $valuesometimes we say, "There are two commands, 'set' and 'puts'," and sometimes we say, "There are two command (list)s, 'set a value', and 'puts $value'." I now believe this ambiguity, or at least polysemy, is part of the source of complaints about the complexity of Tcl's syntax.
Wrong: set a [doCmd $b $c] # doCmd affects global state Need: set a [doCmd $b $c] ;# doCmd affects global stateComments near cases are notorious in Tcl for causing embarrassing problems. Bryan Oakley posted particularly illuminating explanations--including one that Nir Levy labeled "the best example ever"--of the situation. Here's an abbreviation of the main idea:
switch $a in { # This comment in the obvious place causes unexpected effects fred { doFred } fred { # this comment placement is fine if non-obvious doFred } default { doDefault } }More comments on comments appear in the Wiki.
/* This is C. */ myvariable = 3; printf("%d\n", myvariable);
# This is Perl. $myvariable = 3; printf $myvariable;
# This is Tcl. set myvariable 3 puts $myvariableIt is possible to write "set $var 23", but only in such a context as
set var myvariable set $var 23 puts $myvariable; # This will print "23".A related common confusion is with double de-referencing.
print $$myvariable;
.
The analogue
most Tcl beginners try doesn't give them what they want.
As Joe Moss
explains,
puts [set $myvariable]
does.
There's more to it, though.
button .button -text Button pack .button bind . <Enter> {push "I hit 'Enter'."}This does not give the results they want, for "Enter" in the context of binding has to do with moving the cursor within the geometric boundary of a limit. It's almost certain that such programmers want
button .button -text Button pack .button bind . <Return> {push "I hit 'Enter', also called 'Return'."}
set number_of_lines $argv set filename /tmp/example set fp [open $filename w] for {set i 1} {$i <= $number_of_lines} {incr i} { puts $fp "This is line #$i." } close $fp set fp [open $filename] while {![eof $fp]} { puts [gets $fp] } puts "Did you notice that trailing blank line?" close $fp set fp [open $filename] while {-1 != [gets $fp line]} { puts $line } puts "*This* coding does what readers expect."illustrates how this frequently leads to an off-by-one error. On another hand, this fault often is inconsequential, for many programs report the same result even if (unintentionally) coded, as in this example, to receive one apparent extra blank line at the end of the file read.
exec ls -l /bindoes what Unix people want, but
set cmd "ls -l /bin"; exec $cmddoes not, but
set cmd "ls -l /bin"; eval exec $cmdand even
set cmd "ls -l /bin"; exec /bin/sh -c $cmddo. Microsoft hostings (Windows 3.1, W95, WNT, ...) present particularly puzzling manifestations of this same:
exec dirand
exec \msdev\bin\nmakedon't yield happy results, but
exec command.com /c dir(or, even better,
exec $env(COMSPEC) /c dir, or still better than that,
eval exec [auto_execok dir]) and
exec /msdev/bin/nmakedo. [Explain why] [See DIR.] [See long explanation of exec and relatives.] Repeat: "exec doesn't use a shell."
Many exec solutions turn out to involve catch.
expr [info exists a] ? $a : 0probably doesn't do what's wanted, but
expr {[info exists a] ? $a : 0}will.
[Anyone have a punchy way to explain this?]
Tcl 8.0 might change all this; the plan is to treat all expressions as if they are in braces, making the two examples identical.
... open $MYFIFO {RDONLY NONBLOCK}It's even possible to fconfigure the channel back to a blocking mode, once the open has been achieved.
proc my_command {} { global local_variable set local_variable something } my_command puts "$local_variable is OK."This usage is opposite what many coming from C expect, because the
global
appears in (local)
procedures, rather than "on top".
[explain other global problems]
The Wiki has more explanations.
if ($a < $b) ...and
if {a < b} ...are probably mistakes, but
if {$a < $b} ...probably isn't. Recall that the first argument to "if ..." is an expression to be evaluated. Another common syntax-related error that often plagues those familiar with a particular C coding style is
if {$a < $b} { do something; }for which
if {$a < $b} { do something; }or even
if {$a < $b} \ { do something; }is probably wanted.
expr 1234567890 --> 1234567890 expr 12345678901 --> -539222987As Dr. Ousterhout explained in Message-ID <58svee$f77@engnews2.Eng.Sun.COM>,
I'm not sure that this is documented anywhere, but the truth is that Tcl integers are always at least 32 bits, and sometimes more. The "sometimes more" part is a bit unpredictable because it depends on the platform and on the particular command being invoked. If the C "int" type is 64 bits on a platform then Tcl integers should always be 64 bits. However, there are some platforms (DEC Alphas?) where "int" is 32 bits but "long" is 64 bits. On these platforms expr will do its calculations using 64 bits but almost everything else in Tcl only uses 32 bits (i.e. Tcl uses the C type "int" everywhere except in expressions, where "long" is used; don't ask why).
my_command
. To mimic the same within a
batched application, simply puts [my_command]
.
Similarly, when a command works interactively, but
reports "invalid command ..." in batch, it's probably
because of the following: when an interpreter does not
recognize an invoked command, it attempts to resolve it
by a couple of different conventions. One is to regard
the command as an external command, and invoke it through
exec.
Thus, for example, ls
returns a result when
run in an interactive Unix session, but can't be found as
a command in batch, where it's necessary to be explicit:
exec ls
.
There are other reasons
to deprecate exec ls
, incidentally.
lreplace list $first $lastwhen they want
set list [lreplace $list $first $last]
The '*.c' is not expanded. ls
is trying
to find a file called '*.c'. What you really want is
one of the following:
tclsh % eval ls [glob *.c] tclsh % eval exec ls [glob *.c] ;# ok in scripts, too tclsh % exec /bin/sh -c "ls *.c" ;# ok in scripts, too
Unlike most UNIX shells, Tcl does NOT expand patterns such as "project*.tcl" in file names. In particular, the operation of exec is somewhat like MS-DOS's COMMAND.COM in this regard. A result is that, whereas in {sh,bash,csh,...} you'd write
find . "*.c" -printin order to ensure find sees the pattern, Tcl permits you simply to command
exec find *.c -printYou often want pattern (sometimes called "wildcard") expansion, though. The
glob
command provides this. If you'd write
lpr *.cin shell, for Tcl you'd have
exec lpr [glob *.c]The manual page explains that [glob] generates an error if no files match unless you specify
-nocomplain
.
One detail missing from the man page is that
[glob */]returns a list of subdirectories of the current directory whereas
[glob *]gives a list of files AND subdirectories.
set my_variable a_value proc my_command {} { puts "The value is '$my_variable'." } my_commandwe say that
my_variable
is known only in
global
context, but not in the context of my_command
.
You'll be closer to what you want if you write
set my_variable a_value proc my_command {} { global my_variable puts "The value is '$my_variable'." } my_commandWarning: you're likely to come across source code that looks like
set my_variable a_value proc my_command {} { uplevel {puts "The value is '$my_variable'."} } my_commanduplevel is a topic you should tackle only after you're quite comfortable with global.
glob [A-z]*is BAD because Tcl tries to interpret A-z as a command, but
glob \[A-z]*is better, although still problematic if used within another command. For example,
lsort [ glob \[A-z]* ]matches the first [ with the first closing ]. What's probably wanted is
lsort [ glob \[A-z\]* ]or
lsort [glob {[A-z]*}]
[glob $wildcard]
for
$filename
.
awk
, which behaves identically,
for these purposes.
exec /Program Files/../myprogram.exethey see
couldn't execute "\Program" ...What they generally want is
exec {/Program Files/../myprogram.exe}Paul Duffin has an interesting approach to letting Tcl itself figure out such quoting.
regexp {[^\n]+}looks for strings not containing backslash or n. This is symptomatic of a whole class of uneasy interactions between regexp and Tcl syntax, which many Tcl-ers cite as the single biggest headeache when writing Tcl: "what to quote?"
The most common difficulty for new Tcl users is understanding when substitutions do and do not occur. A typical scenario is for a user to be surprised at the behavior of a script because a substitution didn't occur when the user expected it to happen, or a substitution occurred when it wasn't expected. However, I think that you will find Tcl's substitution mechanism to be simple and predictable if you just remember two related rules:My summary: don't fight the substitution rules; spending much time worrying about substitution and escaping is usually a clue that it's time to decompose your problem with judicious definition of a new procedure or two.
- Tcl parses a command and makes substitutions in a single pass from left to right. Each character is scanned exactly once.
- At most a single layer of substitution occurs for each character; the result of one substitution is not scanned for further substitutions.
One other aspect of syntax often confuses: it's easy to introduce typographical errors--unbalanced parentheses, for example--which are only detected at run-time. This is a surprise to those accustomed to "eager" compilers.
expr [string trimleft $month 0] + 1
This is one of the two most common threads in common.lang.tcl (commenting is the other). The natives are sometimes grouchy on the subject.
set foo_$n "value $n"; ... puts [set foo_$n]it's almost always better engineering to write
set foo($n) "value $n"; ... puts $foo($n)In fact, it's a good rule of thumb to be suspicious any time you see a variable name which is itself variable; that's likely to be an opportunity to engineer a more readable, better-performing, easily re-used, and idiomatic solution with judicious use of associative arrays.
... puts "The 'X' binding now has the value '%X'." ...and %X might evaluate to a string which includes a double-quote character, it's very likely the puts won't do what I want. It's often preferable to write
... puts [list The 'X' binding now has the value '%X'.] ...As Eric Bohlman wrote in <ebohlmanE38px1.AKJ@netcom.com>,
A good general rule is that if you're having substitution problems with a -command script, turning it into a proc will make things easierBryan Oakley summarizes:
In other words, as a general rule it's good to make widget bindings call custom procs rather than hard-coding a bunch of tcl in-line. And, when doing so to use the list command to make a well-formed command.
One programming paradigm that is useful in Tcl that took me a little while to find is using global arrays to manage the global name space, and make access to global config variables easier from deep inside procedure calls.[Construct good example.]
button $buttonname -command "puts $mymessage", as many of us often do, we're asking for less useful handling of '$', '[', blank, and other special characters in $mymessage than
button $buttonname -command [list puts $mymessage]affords. However, there's subtlety even here, most notably in the case when we want variable substitution at both binding- and execution-time. The example then might be
... -command "puts \$fid [list $mymessage]"
The first unmatched double quote or brace will kill your application.
set name pt-004.tallships.i scan $name {pt-%d.talls} nwith
regexp {pt-0*([0-9]+)\.talls} $name {} nScan is also handy for populating a collection of variables, in analogy with Perl's XXX:
scan [clock format [clock seconds] -format {%Y %m %d}] {%d %d %d} y m dHowever, scan isn't as powerful as regexps in generalizing, and sometimes those coming from C or other languages try too hard to make it work, when a simple regexp would be easier [write good example]. See also format for related information.
set string /export/home/dickenp/mail2trace2 set strip /export/home/dickenp/ string trimleft $string $stripreturn 'ail2trace2' return than mail2trace2?" As Mike Hoegemann writes, "the last argument to string trimleft is a *set* of characters to be trimmed NOT a suffix pattern to be trimmed." This surprises quite a few programmers, though.
while $var {...}It's almost certain that
while {$var} {...}is intended, for the first form is only an obscure way to loop for(n)ever. In the first form, $var is evaluated once, and that same value is used on each iteration. In the second form, the current value of $var is tested on each pass through the loop.
It's ENORMOUSLY helpful to hear from readers about whether and how this article benefits them. I'm excited about the possibility that careful cross-referencing can make this article particularly useful as a tutorial. Another live question for me now has to do with the scale of this article; I suspect it's just a bit large to be comprehended easily, and deserves a re-write which will pare down its top-level size. Reactions, anyone?