file exists

file exists checks for the existence of a file.


file exists name


Returns 1 if file name exists and the current user has permission to access information about the file, or 0 otherwise. If file is a symbolic link, it is the target that is considered.

The following procedure is a variant of file exists that also returns 1 for broken symbolic links:

proc lexists name {
    expr {![catch {file lstat $name finfo}]}

Trailing Slashes Ignored

Note: if [file exists $a] && ([file type $a] == "file") then [file exists ${a}/], which seems inconsistent.

In normal usage, it wouldn't bother anyone, but in VFS work, there can be a world of difference between a file "$a", a directory "$a", and a file "$a/". I suspect this stems from [file split a/b] == {a b} != {a b {}} - 20040613 CMcC

PYK 2018-03-24: And what exactly does $a/ mean, if it doesn't mean $a?

Case Sensitivity

MB : I have a problem on using the file exist command under windows XP while processing file names. The following script exhibits the problem. When one file "myfile.txt" is created, the file exist command says that the file "MYFILE.txt" exists, which is wrong since the two files have different name cases. I wonder if this is a portability problem because of the Windows XP platform or an internal bug of the Tcl command file exist.

# Test to know whether windows can take the case into account
# in the file names
# 1. Create a new directory
# 2. Create a new file in that directory
# 3. Check that under windows the Tcl command "file exist" returns a wrong value.
package require fileutil
set tmpdir [fileutil::tempdir]
set newdir [file join $tmpdir "dirtotest"]
file mkdir $newdir
set filename [file join $newdir "myfile.txt"]
puts "Touching $filename"
fileutil::touch $filename
set fexist [file exist $filename]
puts "File exist : $fexist"
set otherfilename [file join $newdir "MYFILE.txt"]
puts "Other file : $otherfilename"
set fexist [file exist $otherfilename]
puts "Other file exist : $fexist"
# Check that the other file is not is the list returned by "glob"
set listOfFiles [glob -directory $newdir *]
puts "Files : $listOfFiles"
set found [lsearch $listOfFiles $filename]
puts "Found $filename in the directory $newdir : $found"
set found [lsearch $listOfFiles $otherfilename]
puts "Found $otherfilename in the directory $newdir : $found"
# Clean-up
file delete -force $newdir

This script generates the output :

Touching C:/WINDOWS/Temp/dirtotest/myfile.txt
File exist : 1
Other file : C:/WINDOWS/Temp/dirtotest/MYFILE.txt
Other file exist : 1
Files : C:/WINDOWS/Temp/dirtotest/myfile.txt
Found C:/WINDOWS/Temp/dirtotest/myfile.txt in the directory C:/WINDOWS/Temp/dirtotest : 0
Found C:/WINDOWS/Temp/dirtotest/MYFILE.txt in the directory C:/WINDOWS/Temp/dirtotest : -1

We can see that the returned value of the file exist command is wrong, and that the list returned by glob only shows the file "myfile.txt", which is the least ! I have not tested the same script under Linux, but it would be interesting. What is the explanation ? APN Not sure what discrepancy you are talking about. Windows file paths are not case sensitive so both myfile.txt and MYFILE.txt refer to the same file and file exists correctly returns 1 for both. Your lsearch is failing because the glob returns the file name as stored on disk when created (myfile.txt) and lsearch does case-sensitive matching and fails to match MYFILE.txt.

Broken Symbolic Links

In June 2008, a comp.lang.tcl discussion arose regarding a user who was using file exists on symbolic links and who was frustrated when the results were not what was hoped for.

A poster provided

proc is_broken linkname {
    expr {![file exists [file readlink $linkname]]}

and then later in the program:

set rc [catch {is_broken $filename} result]

if {$rc == 0 && $result} {
    puts "$filename is a broken link"
} elseif {$rc == 0} {
    puts "link $filename is OK"
} else {
    puts "$filename is not a link: $result"

as code which could be used to test whether the symbolic link pointed to an existing file or not.

See Also

file isfile