Input & output
last modified October 18, 2023
In this chapter, we work with input and output operations in Tcl. Tcl has several commands for doing io. We cover a few of them.
Tcl uses objects called channels to read and write data. The channels
can be created using the open
or socket
command.
There are three standard channels available to Tcl scripts
without explicitly creating them. They are automatically opened
by the OS for each new application. They are stdin
,
stdout
and stderr
. The standard input,
stdin
, is used by the scripts to read data. The standard
output, stdout
, is used by scripts to write data. The
standard error, stderr
, is used by scripts to write error
messages.
In the first example, we work with the puts
command. It has the following synopsis:
puts ?-nonewline? ?channelId? string
The channelId is the channel where we want to write text. The
channelId is optional. If not specified, the default stdout
is
assumed.
#!/usr/bin/tclsh puts "Message 1" puts stdout "Message 2" puts stderr "Message 3"
The puts
command writes text to the channel.
puts "Message 1"
If we do not specify the channelId, we write to stdout
by default.
puts stdout "Message 2"
This line does the same thing as the previous one. We only have explicitly specified the channelId.
puts stderr "Message 3"
We write to the standard error channel. The error messages go to the terminal by default.
$ ./printing.tcl Message 1 Message 2 Message 3
Example output.
The read command
The read
command is used to read data from a channel.
The optional argument specifies the number of characters to read.
If omitted, the command reads all of the data from the channel up
to the end.
#!/usr/bin/tclsh set c [read stdin 1] while {$c != "q"} { puts -nonewline "$c" set c [read stdin 1] }
The script reads a character from the standard input channel and then writes it to the standard output until it encounters the q character.
set c [read stdin 1]
We read one character from the standard input channel (stdin).
while {$c != "q"} {
We continue reading characters until the q is pressed.
The gets command
The gets
command reads the next line from the channel, returns everything in the
line up to (but not including) the end-of-line character.
#!/usr/bin/tclsh puts -nonewline "Enter your name: " flush stdout set name [gets stdin] puts "Hello $name"
The script asks for input from the user and then prints a message.
puts -nonewline "Enter your name: "
The puts
command is used to print messages to the
terminal. The -nonewline
option supresses the new line
character.
flush stdout
Tcl buffers output internally, so characters written with puts
may not appear immediately on the output file or device.
The flush
command forces the output to appear immediately.
set name [gets stdin]
The gets
command reads a line from a channel.
$ ./hello.tcl Enter your name: Jan Hello Jan
Sample output of the script.
The pwd and cd commands
Tcl has pwd
and cd
commands, similar to
shell commands. The pwd
command returns the current working
directory and the cd
command is used to change the working
directory.
#!/usr/bin/tclsh set dir [pwd] puts $dir cd .. set dir [pwd] puts $dir
In this script, we print the current working directory. Then we change the working directory and print the working directory again.
set dir [pwd]
The pwd
command returns the current working directory.
cd ..
We change the working directory to the parent of the current directory.
We use the cd
command.
$ ./cwd.tcl /home/janbodnar/prog/tcl/io /home/janbodnar/prog/tcl
Sample output.
The glob command
Tcl has a glob
command which returns the
names of the files that match a pattern.
#!/usr/bin/tclsh set files [glob *.tcl] foreach file $files { puts $file }
The script prints all files with the .tcl
extension to the
console.
set files [glob *.tcl]
The glob
command returns a list of files
that match the *.tcl
pattern.
foreach file $files { puts $file }
We go through the list of files and print each item of the list to the console.
$ ./globcmd.tcl attributes.tcl allfiles.tcl printing.tcl hello.tcl read.tcl files.tcl globcmd.tcl write2file.tcl cwd.tcl readfile.tcl isfile.tcl addnumbers.tcl
This is a sample output of the globcmd.tcl
script.
Working with files
The file
command manipulates file names and attributes.
It has plenty of options.
#!/usr/bin/tclsh puts [file volumes] [file mkdir new]
The script prints the system's mounted volues and creates a new directory.
puts [file volumes]
The file volumes
command returns the absolute paths to the
volumes mounted on the system.
[file mkdir new]
The file mkdir
creates a directory called new
.
$ ./voldir.tcl / $ ls -d */ doc/ new/ tmp/
On a Linux system, there is one mounted volume—the root directory.
The ls
command confirms the creation of the new
directory.
In the following code example, we are going to check if a file name is a regular file or a directory.
#!/usr/bin/tclsh set files [glob *] foreach fl $files { if {[file isfile $fl]} { puts "$fl is a file" } elseif { [file isdirectory $fl]} { puts "$fl is a directory" } }
We go through all file names in the current working directory and print whether it is a file or a directory.
set files [glob *]
Using the glob
command we create a list of
file and directory names of a current directory.
if {[file isfile $fl]} {
We execute the body of the if
command if the file name in question
is a file.
} elseif { [file isdirectory $fl]} {
The file isdirectory
command determines,
whether a file name is a directory. Note that on Unix,
a directory is a special case of a file.
The puts
command can be used to write to
files.
#!/usr/bin/tclsh set fp [open days w] set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday} puts $fp $days close $fp
In the script, we open a file for writing. We write days of a week to a file.
set fp [open days w]
We open a file named days
for writing. The open
command returns a channel id.
set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}
This data is going to be written to the file.
puts $fp $days
We used the channel id returned by the open
command to
write to the file.
close $fp
We close the opened channel.
$ ./write2file.tcl $ cat days Monday Tuesday Wednesday Thursday Friday Saturday Sunday
We run the script and check the contents of the days file.
In the following script, we are going to read data from a file.
$ cat languages Python Tcl Visual Basic Perl Java C C# Ruby Scheme
We have a simple file called languages in a directory.
#!/usr/bin/tclsh set fp [open languages r] set data [read $fp] puts -nonewline $data close $fp
We read data from the supplied file, read its contents and print the data to the terminal.
set fp [open languages r]
We create a channel by opening the languages file in a read-only mode.
set data [read $fp]
If we do not provide a second parameter to the read
command, it reads all data from the file until the end of the file.
puts -nonewline $data
We print the data to the console.
$ ./readfile.tcl Python Tcl Visual Basic Perl Java C C# Ruby Scheme
Sample run of the readfile.tcl
script.
The eof
command checks for end-of-line of
a supplied channel.
#!/usr/bin/tclsh set fp [open languages] while {![eof $fp]} { puts [gets $fp] } close $fp
We use the eof
command to read the contents of
a file.
while {![eof $fp]} { puts [gets $fp] }
The loop continues until the eof
returns true if
it encounters the end of a file. Inside the body, we use the
gets
command to read a line from the file.
$ ./readfile2.tcl Python Tcl Visual Basic Perl Java C C# Ruby Scheme
Sample run of the readfile2.tcl
script.
The next script performs some additional file operations.
#!/usr/bin/tclsh set fp [open newfile w] puts $fp "this is new file" flush $fp file copy newfile newfile2 file delete newfile close $fp
We open a file and write some text to it. The file is copied. The original file is then deleted.
file copy newfile newfile2
The file copy
command copies a file.
file delete newfile
The original file is deleted with the file delete
command.
In the final example, we work with file attributes.
#!/usr/bin/tclsh set files [glob *] set mx 0 foreach fl $files { set len [string length $fl] if { $len > $mx} { set mx $len } } set fstr "%-$mx\s %-s" puts [format $fstr Name Size] set fstr "%-$mx\s %d bytes" foreach fl $files { set size [file size $fl] puts [format $fstr $fl $size] }
The script creates two columns. In the first column, we have the name of the file. In the second column, we display the size of the file.
foreach fl $files { set len [string length $fl] if { $len > $mx} { set mx $len } }
In this loop, we find out the most lengthy file name. This will be used when formatting the output columns.
set fstr "%-$mx\s %-s" puts [format $fstr Name Size]
Here we print the headers of the columns.
To format the data, we use the format
command.
set fstr "%-$mx\s %d bytes" foreach fl $files { set size [file size $fl] puts [format $fstr $fl $size] }
We go through the list of files and print each file name and its
size. The file size
command determines the size of
the file.
$ ./attributes.tcl Name Size attributes.tcl 337 bytes newfile2 17 bytes allfiles.tcl 75 bytes printing.tcl 83 bytes languages 51 bytes hello.tcl 109 bytes days 57 bytes read.tcl 113 bytes files.tcl 140 bytes globcmd.tcl 82 bytes write2file.tcl 134 bytes doc 4096 bytes cwd.tcl 76 bytes tmp 4096 bytes readfile.tcl 98 bytes isfile.tcl 219 bytes
Sample run.
In this chapter, we have covered Input/Output operations in Tcl.