Home  Contents

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

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 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.