411 REFERENCE · Wikibooks
Tcl Programming/Working with files
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
__TOC__
Files and channels
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
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 ... argNTcl 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
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 $handleC'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
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
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