Input & output
In this chapter, we will work with input, output operations in Tcl. Tcl has several commands for doing io. We will 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, which are 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 will 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.
$ cat print.tcl #!/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.
$ ./print.tcl Message 1 Message 2 Message 3
Output.
In the next example, we will be reading from the standard input channel.
#!/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. But the command can write to any channel, not just
terminal.
flush stdout
Tcl buffers output internally, so characters written with puts
may not appear immediately on the output file or device.
We can force output to appear immediately with the
flush command.
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 read command is used to read data from the channel.
#!/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 a 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.
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 will print the current working directory. 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/vronskij/programming/tcl/io /home/vronskij/programming/tcl
Output.
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 .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.
$ ./glob.tcl isfile.tcl readfile.tcl attributes.tcl allfiles.tcl cwd.tcl addnumbers.tcl glob.tcl write2file.tcl files.tcl
An example from a certain 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.
Next, we are going to write to a file.
#!/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
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 command.
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 will 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 isfile.tcl 219 bytes readfile.tcl 98 bytes tmp 4096 bytes attributes.tcl 337 bytes allfiles.tcl 75 bytes read.tcl~ 114 bytes cwd.tcl 76 bytes
Sample run.
In this chapter, we have covered Input/Output operations in Tcl.