Tcl Programming/Working with files

Files and channels

edit

In addition to the functionalities known from C's stdio, Tcl offers more commands that deal with files, similar to what shells provide, though often a bit more verbose. Here are some examples:

glob *.tcl

List all files in the current directory that match the pattern *.tcl.

file copy /from/path/file.ext /to/path
.
file delete /from/path/file.ext
.
file rename before.txt after.txt
.
cd /an/other/directory
.
pwd

To let code temporarily execute in another directory, use this pattern:

set here [pwd]
cd $someotherdir
#... code here
cd $here

More precisely, many "file" operations work in general on "channels", which can be

  • standard channels (stdin, stdout, stderr)
  • files as opened with open ...
  • pipes as opened with 'open |...
  • sockets (TCP)

File names

edit

Files are often addressed with path names, which indicate their position in the directory tree. Like Unix, Tcl uses "/" as path separator, while on Windows "\" is the native way - which brings trouble not only to Tcl, but even to C, because "\" is the escape character on both, so that e.g. \t is parsed as horizontal tab, \n as newline, etc. Fortunately Windows accepts natively "/" as well, so you can use forward slash in both Tcl and C programs as path separator without worries. However, you still have to take care of escape sequences. One stopgap measure is

  • to escape the escape character, i.e. write \\ for \, or
  • brace backslashed names, e.g. {\foo\bar\grill.txt}

But Tcl allows to use the "normal" separator / in almost all situations, so you're safer off with that. Unfortunately, things are sad for Mac users, since MacOS (before X) accepts only ":" as file separator.

If you need to, here's ways to convert between the two forms:

% file normalize \\foo\\bar\\grill.txt
C:/foo/bar/grill.txt
% file nativename /foo/bar/grill.txt
\foo\bar\grill.txt

You can even use file join command: file join arg1 arg2 ... argN

Tcl will then take care of all platform dependent details to create platform independent path. For example:

set somepath [file join foo bar grill.txt]

will result in following path (on windows machine): foo/bar/grill.txt

Input and output

edit

Tcl's input/output commands are pretty closely based on those from C's stdio (just strip off the leading f):

  • set handle [open filename ?mode?]
  • set data [read $handle ?int?]
  • tell $handle
  • seek $handle offset ?from?
  • puts ?-nonewline? ?$handle? content
  • gets $handle ?varname?
  • close $handle

C's printf functionality is split in two steps:

  • format the data into a string with format (which is very much like sprintf)
  • output the resulting string with puts. For example,
puts $handle [format "%05d %s" $number $text]

To process a text file line by line, you have two choices. If the file is smaller than several megabytes, you can read it just in one go:

set f [open $filename]
foreach line [split [read $f] \n] {
    # work with $line here ...
}
close $f

For files of any big size, you can read the lines one by one (though this is slightly slower than the above approach):

set f [open $filename]
while {[gets $f line] >= 0} {
    # work with $line here ...
}
close $f

Finally, if you can format your file so that it is executable Tcl code, the following reading method is fastest:

source $filename

To "touch a file", i.e. create it if not exists, and in any case update its modification time, you can use this:

proc touch name {close [open $name a]}

"Binary" files

edit

All files are made of bytes, which are made of bits, which are binary. The term "binary" with files relates mostly to the fact that they can contain bytes of any value, and line-ends (Carriage Return+Newline in the DOS/Windows world) are not to be translated. Tcl can handle "binary" files without a problem -- just configure the channel as binary after opening:

set fp [open tmp.jpg]
fconfigure $fp -translation binary
set content [read $fp]
close $fp

Now the variable content holds the file's contents, byte for byte.

To test whether a file is "binary", in the sense that it contains NUL bytes:

proc binary? filename {
   set f [open $filename]
   set data [read $f 1024]
   close $f
   expr {[string first \x00 $data]>=0}
}

The file command

edit

Many useful operations with files are collected in the file command. The first argument tells which operation to do:

  • file atime name ?time?
  • file attributes name
  • file attributes name ?option?
  • file attributes name ?option value option value...?
  • file channels ?pattern? - returns the handles of currently open files
  • file copy ?-force? ?- -? source target
  • file copy ?-force? ?- -? source ?source ...? targetDir
  • file delete ?-force? ?- -? pathname ?pathname ... ?
  • file dirname name - e.g. [file dirname /foo/bar/grill.txt] -> /foo/bar
  • file executable name
  • file exists name
  • file extension name - e.g. [file extension /foo/bar/grill.txt] -> .txt
  • file isdirectory name
  • file isfile name
  • file join name ?name ...?
  • file link ?-linktype? linkName ?target?
  • file lstat name varName
  • file mkdir dir ?dir ...? - creates one or more directories (folders)
  • file mtime name ?time?
  • file nativename name
  • file normalize name
  • file owned name
  • file pathtype name
  • file readable name
  • file readlink name
  • file rename ?-force? ?- -? source target
  • file rename ?-force? ?- -? source ?source ...? targetDir
  • file rootname name - e.g. [file rootname /foo/bar/grill.txt] -> /foo/bar/grill
  • file separator ?name?
  • file size name
  • file split name - e.g [file split /foo/bar/grill.txt] -> {foo bar grill.txt}
  • file stat name varName
  • file system name
  • file tail name - e.g. [file tail /foo/bar/grill.txt] -> grill.txt
  • file type name
  • file volumes - Windows: returns your "drive letters", e.g {A:/ C:/}
  • file writable name