Tcl Part I · Module 1 · Introduction to Tcl
puts: Making a Script Say Something
You have been using this command on trust since the first page, and it earns a chapter of its own for two reasons. It is your only window into a running script — Tcl throws away every result you do not explicitly ask to see, so until much later in the track `puts` is how you find out what your program is doing. And it is the first command whose behaviour visibly depends on Tcl splitting your line into words, which means a small change in quoting does not merely change the output, it can change which arguments the command thinks it received. The error you get when that happens is unusually informative, and reading it is the most efficient way to learn what this command actually accepts.
Foundation13 min readTclPart IFundamentalsputsOutput
Part I · Module 1 · Chapter 1.7 · puts: Making a Script Say Something
1. Why This Command Matters More Than It Looks
Chapter 1.5 §3 established something that makes this command essential rather than merely useful: a Tcl script discards the result of every command unless you do something with it. Compute a value, and it is gone. A script can do a great deal of correct work and tell you nothing.
So puts is not just "how to print". For the whole of Part I it is the only way to observe a running program. When a script misbehaves, the fastest diagnostic available is to add a puts and look at what is actually there rather than what you assumed.
Worth learning properly, then.
2. The Simplest Form
One argument — text to display, followed by a line break:
puts "starting the check"starting the checkThe quotes are not part of the message. They group the words starting, the and check into a single argument so that puts receives one piece of text rather than three.
That claim is easy to state and easy to disbelieve, so the next section makes it visible.
3. The Quotes Are Doing Real Work
Try a single word with no quotes at all:
puts hellohelloThat works. One word is one argument, so grouping it changes nothing.
Now two words with no quotes. Predict the output before reading on:
puts hello thereMost people expect hello there. Here is what actually happens:
can not find channel named "hello"
while executing
"puts hello there"
(file "two-words.tcl" line 1)Read that error closely, because it is telling you something precise. Tcl split the line into three words — puts, hello, there — and handed puts two arguments. Given two arguments, puts treats the first as somewhere to write and the second as the text. So it looked for a destination called hello, and there is no such thing.
Nothing was misspelled. The command received exactly what you typed; your assumption about how it would be grouped was wrong.
Add a third word and the error changes again:
puts hello there worldbad argument "world": should be "nonewline"
while executing
"puts hello there world"
(file "three-words.tcl" line 1)Three arguments now, and puts expects the first of three to be an option. hello is not an option, so it complains — naming the only option it accepts, which is useful information we will use in §7.
With quotes, all of this goes away:
puts "hello there world"hello there worldOne argument, because the quotes grouped it.
4. Ask the Command What It Takes
There is a shortcut to the signature, and it is worth knowing as a technique rather than a trick. Call puts with no arguments at all:
putswrong # args: should be "puts ?-nonewline? ?channelId? string"
while executing
"puts"
(file "nothing.tcl" line 1)Tcl answered with the command's own usage line. Reading Tcl's convention:
?...?marks an optional part. So-nonewlineandchannelIdare both optional.stringhas no question marks, so it is required.
That one line explains every result in §3. puts hello there supplied channelId and string; puts hello there world supplied all three and got the option wrong.
5. Where the Text Goes
The optional channelId is a destination. Two are open for you from the start:
stdout— standard output, the default. This is where your results go.stderr— standard error, intended for diagnostics and problems.
These two lines are equivalent in effect:
puts "to standard output"
puts stdout "to standard output"The explicit form is rarely written, because the default is what you want nearly always. Its value is in making §3 comprehensible: stdout occupies the same argument slot that hello wrongly landed in.
6. Why stderr Is Worth Using
Both streams normally appear in your terminal, so on screen the distinction looks like nothing:
puts "the result: 42"
puts stderr "warning: input file was empty"the result: 42
warning: input file was emptyThe difference appears the moment anything other than a human consumes the output. The two streams can be separated. Keeping only the results:
tclsh streams.tcl 2>/dev/nullthe result: 42Keeping only the diagnostics:
tclsh streams.tcl 2>&1 >/dev/nullwarning: input file was emptyThat is the whole point. If a script's results and its commentary go to the same stream, anything processing those results has to somehow tell them apart. If they are separated at the source, it does not.
The rule is simple enough to adopt permanently:
What the script is for goes to
stdout. What the reader needs to know about the run goes tostderr.
7. Suppressing the Line Break
puts normally ends with a newline. The one option it accepts turns that off:
puts -nonewline "checking "
puts -nonewline "and checking "
puts "done"checking and checking doneThree commands, one line of output. The first two wrote without a line break, and the third ended it.
Order matters when combining the option with a channel — the option comes first, exactly as the usage line in §4 shows:
puts -nonewline stderr "working... "
puts stderr "finished"working... finished8. Failure Modes, Collected
Four, and you have met three already.
| What you wrote | What you get | Why |
|---|---|---|
puts hello there | can not find channel named "hello" | Two arguments: the first is read as a destination. Quote the text. |
puts hello there world | bad argument "world": should be "nonewline" | Three arguments: the first must be the option. Quote the text. |
puts | wrong # args: should be "puts ?-nonewline? ?channelId? string" | string is required. Usefully, this prints the usage. |
puts nosuchchannel "text" | can not find channel named "nosuchchannel" | That destination does not exist. |
Three of the four are the same mistake — unquoted text being split into separate arguments. That is worth noticing, because it is the shape of a large fraction of all Tcl beginner errors, not just this command's.
9. Exercises
Exercise 1 — reproduce all three errors. In a file, trigger puts hello there, then puts hello there world, then puts alone. One at a time, since the first one stops the script. Read each message and name which argument slot caused it.
Exercise 2 — derive the behaviour from the usage line. Using only puts ?-nonewline? ?channelId? string, predict what each of these does. Then run them.
puts stdout hello
puts -nonewline hello
puts -nonewline stdout helloExercise 3 — separate the streams. Write a script that prints one result line and two diagnostic lines, with the results on stdout and the diagnostics on stderr. Verify by running it twice, once with 2>/dev/null and once with 2>&1 >/dev/null, and confirm each shows only what it should.
Exercise 4 — one line from three commands. Produce the output a-b-c followed by a line break, using exactly three puts commands and no text containing more than one letter. You will need -nonewline and the separators.
Exercise 5 — use the technique, not the chapter. You have not been taught string length. Without searching, call string with no arguments and then string length with no arguments, and from the usage lines alone work out how to display the length of the text report.txt. This is §4 used the way it is meant to be used.
10. Summary
putsis your only window into a running script. Tcl discards results you do not ask to see.- The usage is
puts ?-nonewline? ?channelId? string.?...?means optional;stringis required. - Spaces separate arguments; quotes group text into one argument.
puts hello therepasses two arguments, not one piece of text. - The resulting errors disclose the signature.
can not find channel named "hello"is Tcl telling you the first of two arguments is a destination. - Calling a command with no arguments usually prints its usage. Faster than looking it up.
stdoutis for what the script is for;stderris for what the reader needs to know about the run. The streams can be separated downstream, which is the whole reason to keep them apart at the source.-nonewlinesuppresses the line break, and must come before a channel. Unterminated-nonewlineoutput can sit in a buffer rather than appearing.- Three of the four common failures are the same mistake: unquoted text split into separate arguments.
The next chapter deals with the other thing you have been writing without explanation — comments — and with a rule about them that is specific to Tcl and catches everybody exactly once.
Where this fits
Part of the Tcl curriculum.
