'''file join''' ''name ?name ...?'' Takes one or more file names and combines them, using the correct path separator for the current platform. If a particular ''name'' is relative, then it will be joined to the previous file name argument. Otherwise, any earlier arguments will be discarded, and joining will proceed from the current argument. For example, file join a b /foo bar returns '''/foo/bar'''. Note that any of the names can contain separators, and that the result is always canonical for the current platform: '''/''' for Unix and Windows, and ''':''' for Macintosh. ---- The '''file join''' command joins zero or more strings with the correct platform-dependent separators so that the result can be interpreted as a path name. ---- One useful application is to "normalize" a Windows pathname with backslashes to slashes, which is more robust in all kinds of intermediate processing, where substitution might occur: % file join {\foo\bar\grill} /foo/bar/grill Back the other way with ''file nativename'': % file nativename /foo/bar/grill \foo\bar\grill [DGP]...but '''[file normalize]''' is a better tool for that purpose now. ---- One way to convert a file type from (possibly) relative to absolute is this: set filename [file join [pwd] $filename] Note that you never need to use [cd], and it is best if you don't except in response to user action. Otherwise you just confuse your users... :^/ [DKF] ----- [MSW] The most wonderful property about file join though is that it's so useless on lists that it hurts. puts [file join [file split a/b/c]] => a b c ---- I apologise for interjecting at this particular point, but what's wrong with the following? puts [eval file join [file split a/b/c]] => a/b/c [MSW] It uses [eval] for list-splicing which is more than ugly (and evil :). Again, it's the asymmetry, imagine you'd have to use an '''evil''' for each of your [join] calls... *shudder* plus read on ... You could also say that tcl being too dumb to tell a list and a string with spaces apart is the real reason. Now that we're having objects, can't we tag lists as such please ? puts -nonewline "Enter a path for the savefile :" flush stdout set p [gets] ;# here you enter /home/[file delete -force [glob -nocomplain ~]; return $::env(USER)]/savefile # check if we're leaving our subtree, if not, store # a relative path only ... some file split, list checking, alter some early elements of the list ... eval file join ... ;# enjoy... I'll use [join] in the meantime. ---- I've been bitten by that more than once (e.g. when pruning away common parent-trees in paths, idea: set path a/b/c/d/e/f/g; set common_length 3 set path [file join [lrange [file split $path] $common_length end]] => d e f g NOT d/e/f/g!! ...it just stinks. *cry* You end up using file split, join $path /, instead. Of course that means you're using native directory separators, which stinks nearly more ... but works. And honestly, I hate to use eval for list splicing :) Basically I don't ''want'' to splice the list, file join should '''just work'''! [RS] It works as documented. You want a different behavior, that it joins the elements of a list. But this can lead to ambiguities, consider file join "C:/Program Files/Tcl" which can be seen as a 2-element list... Also, you don't have to name the separator, Tcl knows: % join {foo bar grill} [file separator] foo\bar\grill [MSW] True, I've stumbled over that so often though .. *mutter* % file separator bad option "separator": must be atime, attributes, channels, copy, delete, dirname, executable, exists, extension, isdirectory, isfile, join, lstat, mtime, mkdir, nativename, owned, pathtype, readable, readlink, rename, rootname, size, split, stat, tail, type, volumes, or writable [RS] Well, yes, new feature, 8.4.2 has it. You can always fall back to proc file'separator {} { switch -- $::tcl_platform(platform) { unix {return /} windows {return \\} macintosh {return ::} default {error "unknown platform"} } } [slebetman]: Edited the code above to support Mac Classic. Otherwise all recent versions of the MacOS identify themselves as unix. [MSW] 8.4 is not an option for me (yet) as tix won't work with it, and all the CONSTifying shoots in my C-linkage heritage ... You don't like macintosh, eh ? :) But well, the ambigious list/string problem haunts us in tcl all the time... Even [[[list] "a b"]] ends up as a two element list (even though [list] clearly should create a one element list whose element can be interpreted as two element list ..) '''(1)'''. Ah well ... ''That is incorrect. [[list "a b"]] returns a one element list, as it should. Let's not spread false information.'' % set a [list "a b"] {a b} % llength $a 1 Furthermore, I guess my real point didn't really get through. The [file] subcommands are not symmetric, and that stinks, no matter how well this misbehaviour is documented :) set x "a-b-c" set y [join [split $x -] -] if {$x != $y} then { error "braindead" } set x "a/b/c" set y [file join [file split $x]] if {$x == $y} then { error "miracle" } Now imagine [join] would behave like [file join] ... yuck ... ---- (1) [RS]: It does exactly what you want: % set foo [list "a b"] {a b} % llength $foo 1 ---- [MSW]: Urps yes sorry. Dunno how, but yesterday it said 2 for me. I swear ! :p ---- Windows programmers may be surprised to find that [[file join "c:" "test"] does not necessarily (or perhaps ever?) mean "create a root directory called test on the C drive. Instead, it means "create a directory called test on the C drive. Make it in the current working directory." ''[MG] notes that [[file join]] doesn't make the directories, it just provides a path which you can pass to [[file mkdir]]. But the path which would refer to a root directory "test" would be [[file join "c:/" "test"]] - you need that / make it work right.'' ---- How can one overcome the tilde (~) substitution in file join on Windows? I want to join a file which starts with a tilde ~filename.ext with the current pwd. [[file join [[pwd]] ~filename.ext]] gives ~filename.ext, which is good Unix shell behavior, but not helpful in Windows. [Vince] -- I think you'll want [[file join [[pwd]] ./~filename.ext]] . I'm not sure what the correct cross-platform version is. [jmn] 2006-08-16 -- This has bitten me. Quite the nasty surprise really. using [[file join $folder ./$name]] everywhere is ugly & unintuitive.. and means you end up with silly (and inefficient) looking paths such as c:/./blah/./etc just to handle the occasional situation where a file starts with a tilde. Ok.. the strange looking paths are basically just intermediate results and get cleaned up with a [[file normalize]] but it's still rather unsatisfactory. I've gone as far as testing the first character of every filename before 'file joining'.. also doesn't make for neat or efficient code. Any better ideas? Any votes for a '-homeless' flag or equivalent to stop the ~ substitution? I bet there are a lot of scripts out there that are broken in this regard.. just waiting to come across files prefixed with ~ to keel over. At the very least I think this needs to be flagged brightly in documentation. ---- [RS]: If you don't need to support Mac classic, "/" is the canonical separator for all Unixes and Windowses, so in your case $folder/$name should help., and is [simple]r than the [file join]ery. ---- felipel 2007-01-30 -- I thought of a patch solution. Basically, it consists in that we should deliver arguments to [file join] as a string set mylist [list we are dirs in a list] set mytmppath [join $mynixpath "/"] set myrealpath [file join $mytmppath] # this solves the "is that space or list-element-separator" problem set mywinpath "we\\make\\a\\win\\path" set mynixpath "we/make/a/unix/path" set mytmpstr [join [list $mywinpath $mynixpath] "/"] set myotherpath [file join $mytmpstr] # see how [file join] is still consistent # with your platform # (at least *nix and win) See also: * [file] * [file separator] * [file split] ---- [Tcl syntax help] - [Category Command] ... well subcommand of [file] - [Category Example] - [Category File]