`'''[http://www.tcl.tk/man/tcl/TclCmd/file.htm%|%file]'''`, a [Tcl Commands%|%built-in] [Tcl] [command], manipulates file names and attributes ** See Also ** [Additional file commands]: [directory recursion]: presents various ways of walking a file hierarchy [filesystem benchmarking]: [How do I remove one line from a file?]: [file and directory change notifications]: ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/file.htm%|%man file]: ** Synopsis ** : '''file''' ''option name ?arg arg ...?'' ** Description ** This command provides several operations on a file's name or attributes. ''Name'' is the name of a file; if it starts with a tilde, then tilde substitution is done before executing the command (see the manual entry for '''[filename]''' for details). ''Option'' indicates what to do with the file ''name''. Any unique abbreviation for ''option'' is acceptable. The valid options are: (see complete man page; here's only notes on selected options). : '''[file atime]''' ''name ?time?'' : '''[file attributes]''' ''name'' : '''[file attributes]''' ''name ?option?'' : '''[file attributes]''' ''name ?option value option value...?'' : '''[file channels]''' ''?pattern?'' : '''[file copy]''' ''?'''''-force'''''? ?'''''--'''''? source target'' : '''[file copy]''' ''?'''''-force'''''? ?'''''--'''''? source ?source ...? targetDir'' : '''[file delete]''' ''?'''''-force'''''? ?'''''--'''''? pathname ?pathname ... ?'' : '''[file dirname]''' ''name'' : '''[file executable]''' ''name'' : '''[file exists]''' ''name'' : '''[file extension]''' ''name'' : '''[file isdirectory]''' ''name'' : '''[file isfile]''' ''name'' : '''[file join]''' ''name ?name ...?'' : '''[file link]''' ''?-linktype? linkName ?target?'' (requires Tcl8.4) : '''[file lstat]''' ''name varName'' : '''[file mkdir]''' ''dir ?dir ...?'' : '''[file mtime]''' ''name ?time?'' : '''[file nativename]''' ''name'' : '''[file normalize]''' ''name'' (requires Tcl8.4) : '''[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'' : '''[file separator]''' ''?name?'' : '''[file size]''' ''name'' : '''[file split]''' ''name'' : '''[file stat]''' ''name varName'' : '''[file system]''' ''name'' (requires Tcl8.4) : '''[file tail]''' ''name'' : '''[file tempfile]''' ?''varName''? ?''template''? (requires Tcl8.6) : '''[file type]''' ''name'' : '''[file volumes]''' : '''[file writable]''' ''name'' <> [[Hopefully someone will provide some samples to show how these are useful. With the new [VFS] support in [Tcl] 8.4, and an appropriate extension, '''file''' can operate on much more than just local files (see the [One-line web browser in Tcl] for example).]] ---- '''Can someone comment on what happens if the current working directory is on a different drive?''' Yes. The question is essentially nonsensical because the current working directory is a per-drive attribute. What happens if I try to open the door, but the other door is open? Hmm , so the following sequence, in Windows, is nonsense? ======none cd C:/tcl/lib set fd [open G:list.txt w] set lst [glob *] puts $fd $lst close $fd ====== Interesting- I never realized that Windows was so brain-damaged. ---- [LES]: One may want to use this [proc] to catch a quick grasp of and/or memorize all the [file] commands: ====== #! /bin/env tclsh proc fileinfo {myFile} { puts "file name: $myFile" puts "exists: [ file exists $myFile ]" puts "----------------" puts "type: [ file type $myFile ]" puts "size: [ file size $myFile ]" puts "" puts "atime: [ file atime $myFile ]" puts "mtime: [ file mtime $myFile ]" puts "" puts "pathtype: [ file pathtype $myFile ]" puts "dirname: [ file dirname $myFile ]" puts "separator: [ file separator $myFile ]" puts "nativename: [ file nativename $myFile ]" puts "normalize: [ file normalize $myFile ]" puts "rootname: [ file rootname $myFile ]" puts "tail: [ file tail $myFile ]" puts "extension: [ file extension $myFile ]" puts "join: [ file join $myFile ]" puts "" puts "attributes: [ file attributes $myFile ]" puts "owned: [ file owned $myFile ]" puts "readable: [ file readable $myFile ]" puts "writable: [ file writable $myFile ]" puts "executable: [ file executable $myFile ]" puts "" puts "channels: [ file channels $myFile ]" puts "system: [ file system $myFile ]" } ====== [IL]: problems identifying what you're looking at? the following proc returns a simple string detailing what you have available to you. This is not great coding, but makes up for some TCL 8.3 ambiguities that might lead a programmer astray due to some assumptions. It also should rid you of the trailing slash problem. ====== proc setloctype { loc { ambigtofile "false" } } { # define a location as a directory or file or both. # also identifies location as absolute or relative # # * manually choose to prefer files over directories when resolving ambiguous references if needed # # examples: # /mydirectory/name - absolute_dir # logs/reports.log - relative_file_dir # /restartfarms.sh - absolute_file set tailtype "" if { [string range $loc end end] == "/" } { set tail "" set loc [string range $loc 0 end-1] set tailtype "dir" } else { set tail [file tail $loc] if { [regexp "\\." $loc] || [string is true $ambigtofile] } { set tailtype "file" } else { set tailtype "dir" } } switch [file pathtype $loc] { "absolute" { switch [regexp -all "/" $loc] { "1" { if { $tailtype == "file" } { set loctype "absolute_file" } else { set loctype "absolute_dir" } } default { if { $tailtype == "file" } { set loctype "absolute_file_dir" } else { set loctype "absolute_dir" } } } } "relative" - default { switch [regexp -all "/" $loc] { "0" { if { $tailtype == "file" } { set loctype "relative_file" } else { set loctype "relative_dir" } } default { if { $tailtype == "file" } { set loctype "relative_file_dir" } else { set loctype "relative_dir" } } } } } return $loctype } ====== ---- [JH]: I wanted to get a relative path based on another path and couldn't find a proc that did it, so here is one I made (with minimal testing): ====== proc relpath {basedir target} { # Try and make a relative path to a target file/dir from base directory set bparts [file split [file normalize $basedir]] set tparts [file split [file normalize $target]] if {[lindex $bparts 0] eq [lindex $tparts 0]} { # If the first part doesn't match - there is no good relative path set blen [llength $bparts] set tlen [llength $tparts] for {set i 1} {$i < $blen && $i < $tlen} {incr i} { if {[lindex $bparts $i] ne [lindex $tparts $i]} { break } } set path [lrange $tparts $i end] for {} {$i < $blen} {incr i} { set path [linsert $path 0 ..] } return [eval [list file join] $path] } return $target } # Some examples relpath C:/Tcl/lib/tcllib C:/Tcl/lib/tcllib/module/pkgIndex.tcl # module/pkgIndex.tcl relpath [file dirname [info nameofexe]] [info library] # ../lib/tcl8.4 relpath C:/Tcl/foo D:/Tcl/bar # D:/Tcl/bar relpath /usr/bin /usr/local/bin/tclsh # ../local/bin/tclsh ====== [MG] Edited so that it runs ''file normalize'' on both paths first - still works the same with all the examples above, but also handles, for instance ======none % cd "c:/program files/tcl/" % relpath . [info library] lib/tcl8.4 ====== when one path is relative and one isn't. I was going to suggest ====== return [join $path [file separator]] ====== instead of ====== return [eval [list file join] $path] ====== to remove the eval, but for me (on Windows XP) that returns a different result - [file separator] returns a '''\''', while [file join] joins using a '''/'''. Using ====== return [file normalize [join $path [file separator]]] ====== would remove the need for `[eval]` and still return the same result as [file join], I think. Is it intended that they (file separator and file join) use different characters on Windows? The docs for both say they use the "native character for the platform"... [JH]: The `[file join]` and `[file separator]` differences are intentional. Note that you can use `[file nativename]` to also get native again. ---- [Csan] How about a [file mkfifo] instead of [exec mkfifo]? Would be a lifesaver for us, un*x users... ---- [MHo]: I'm confused: ====== % glob -types hidden -dir //wa101su052.prod01.hmkintra.de/c\$ * {//wa101su052.prod01.hmkintra.de/c$/boot.ini} {//wa101su052.prod01.hmkintra.de/c$/bootfont.bin} {//w a101su052.prod01.hmkintra.de/c$/Config.Msi} {//wa101su052.prod01.hmkintra.de/c$/IO.SYS} {//wa101su05 2.prod01.hmkintra.de/c$/MSDOS.SYS} {//wa101su052.prod01.hmkintra.de/c$/NTDETECT.COM} {//wa101su052.p rod01.hmkintra.de/c$/ntldr} {//wa101su052.prod01.hmkintra.de/c$/pagefile.sys} {//wa101su052.prod01.h mkintra.de/c$/System Volume Information} % glob -types hidden -dir //wa101su052.prod01.hmkintra.de/c\$ pagefile.sys no files matched glob pattern "pagefile.sys" % file size //wa101su052.prod01.hmkintra.de/c\$/pagefile.sys could not read "//wa101su052.prod01.hmkintra.de/c$/pagefile.sys": no such file or directory % ====== Does this mean we can't operate on hidden and/or system files (MS Win)? While glob lists 'pagefile.sys', the file command ignores it... [LV] On a Windows XP system to which I've access, I tried starting tkcon and then typed ====== (bin) 53 % glob -types hidden -dir $env(HOME) * ====== and I got back a variety of hits. I then typed: ====== (bin) 54 % file size {C:\Documents and Settings\lvirden/NTUSER.DAT} 10747904 ====== So it appears that file can work on files identified as hidden. However, on a Unix system, I tried: ====== $ tclsh8.5 % glob -types hidden -dir /tmp * /tmp/. /tmp/.. /tmp/.A /tmp/.X11-unix /tmp/.X11-pipe /tmp/.__emcp.conf_lock /tmp/.rc7027 /tmp/.dp7027 /tmp/.co7027 /tmp/.rs7027 /tmp/.de7027 % glob -types hidden -dir /tmp .A no files matched glob pattern ".A" % glob -types hidden -dir /tmp /tmp/.A no files matched glob pattern "/tmp/.A" % glob -types hidden -dir /tmp .X11-unix /tmp/.X11-unix % ^D $ ls -ld /tmp/.A drwxr-xr-x 2 root root 178 Sep 6 22:25 /tmp/.A ====== Ah - in this case, glob saw that was not a "file" but a directory. So, I wonder, in your case, if glob is for some reason not seeing pagefile.sys as something other than a file? Perhaps either permissions on the item prevent file from getting the size, or it sees it as a "device" or something instead of a file... Notice that glob kept saying "no files matched"? ---- [LV] Does anyone have any idea how to get functionality comparable to file to work when one's tcl script has to run as a set-userid program? An example of this would be a program which needs to read a sensitive login and password from a file that should not be readable to the user normally. While certainly that one function could be ''farmed out'' to a second program, there are still times when the process needs to be able to perform functions with an effective user-id. [[The catch is that]] [file readable] tests only the real user id and not the effective user id. But it seems like it would be useful for file to have at least an option to check the effective id before checking the real one... ---- [AMG]: A script to check if two strings name the same file would be handy, even in the face of `/.`, `/..`, `~`, `~user`, `//`, relative paths, mixing backslashes on Windows, and combinations thereof resulting in an infinite variety of possible non-canonical representations. Variations include considering symbolic and hard links to be the same or different files. For challenge points, what about the case of the same filesystem being mounted in multiple places? What about Linux --bind mounts and Windows NT junctions [http://superuser.com/questions/77872/how-to-mount-ntfs-folder-in-another-ntfs-folder]? What about Windows drive letters in the multiple ways in which they may be specified or omitted? What about Windows UNC paths which allow multiple ways to name the same share? Things can get arbitrarily hairy. [PYK] 2014-06-05: Here ya go: ====== proc samefile? {fname1 fname2} { file stat $fname1 finfo1 file stat $fname2 finfo2 return [expr $finfo1(ino) eq $finfo2(ino)] } ====== <> <> Tcl syntax | Command | File | Arts and Crafts of Tcl-Tk Programming