[EMJ] Dec 14th 2004 -12-14: I know this isn't really difficult, but if someone's got it somewhere...
If I haGiven twofile file paths a and b, how cadoes one I derive a relative path to refer to a from the location of b (the files and paths may not exist)?
[CMcC]: t There's a [tcllib] [fileutil] ''relative'' and a ''prefix'' command which facilitates this kind of file name manipulation.
[MG] Dec 14th 2004 -12-14: This piqued my curiousity, so I had a quick go at getting
something that works. It's only very lightly tested, but the tests I did worked
OK. This won't work if you try giving it two files on different drives/volumes,
though, and will probably loop indefinaitely;. I wouldn't recommend trying it :D
Someone else may well come up with something neater than this, but in the mean
time...
======
set path1 {C:/Documents and Settings/Griffiths/leaflet.pub}
set path2 {C:/CONFIG.SYS}
proc relTo {a b} {
set a [file dirname [file normalize $a]]
set b [file dirname [file normalize $b]]
set aa [file split $a]
set bb [file split $b] if { [llength $aa] < [llength $bb] } {
set tmp $aa
set aa $bb
set bb $tmp
set switch 1
unset tmp
} else {
set switch 0
}
if { [llength $aa] == [llength $bb] } {
if { $aa == $bb } {
return ".";
}
set i 0
while {$i < [llength $aa]} {
if {[join [lrange $aa 0 end-$i]] == [join [lrange $bb end-$i]]} {
break
} set i 0
while { $i < [llength $aa] } {
if { [join [lrange $aa 0 end-$i]] == [join [lrange $bb end-$i]] } {
break;
}
incr i
}
return [string repeat ".." $i];
}
set i 0
while { [lindex $aa $i] == [lindex $bb $i] } {
incr i
}
set i [expr { [llength $aa] + 1 - $i }]
set sep [file separator]
if { $switch } {
set string .
for {set x 1} {$x <= $i} {incr x} { set string "$string$sep[lindex $aa $x]"
incr i -1
}
return $string; } else {
return "[string repeat "..$sep" [expr {$i-1}]]..";
}
};# relTo
relTo $path1 $path2
% ..\..\..
relTo $path2 $path1
% .\Documents and Settings\Griffiths======
Example:
======none
% relTo $path1 $path2
..\..\..
% relTo $path2 $path1
.\Documents and Settings\Griffiths
======
You can then, for instance,
======
cd [file dirname $path1]
cd [relTo $path1 $path2]
======
to get from the directory of one file to another. And then to get back, cd [relTo $path2 $path1]
[EMJ]======
cd D[reclTo 16$path2 $path1]
======
[EMJ] 2004 -12-16: Thanx very much. Since it won't do up the file tree
and down a different branch, and also was giving funny answers on Linux,
I started to play around with it, eventually ending up with the following,
which does what I want:
======
# get relative path to target file from current file
# arguments are file names, not directory names (not checked)
proc pathTo {target current} {
set cc [file split [file normalize $current]]
set tt [file split [file normalize $target]]
if {![string equal [lindex $cc 0] [lindex $tt 0]]} {
# not on *n*x then
return -code error "$target not on same volume as $current"
}
while {[string equal [lindex $cc 0] [lindex $tt 0]] && [llength $cc] > 1} {
# discard matching components from the front (but don't
# do the last component in case the two files are the same)
set cc [lreplace $cc 0 0]
set tt [lreplace $tt 0 0]
}
set prefix ""{}
if {[llength $cc] == 1} {
# just the file name, so target is lower down (or in same place)
set prefix "."
}
# step up the tree (start from 1 to avoid counting file itself
for {set i 1} {$i < [llength $cc]} {incr i} {
append prefix "{ .."}
}
# stick it all together (the eval is to flatten the target list)
return [eval file join {*}$prefix {*}$tt]
}
======
----'''[AlexD] - 2012-06-03 11:12:23'''
'''[AlexD] 2012-06-03 11:12:23'''
In my case it was better to get relative path to target file from current path (not a file name).
So I did some changes to the code provided by EMJ:
======
# Get relative path to target file from current path
# First argument is a file name, second a directory name (not checked)
proc relTo {targetfile currentpath} { set cc [file split [file normalize $currentpath]]
set tt [file split [file normalize $targetfile]]
if {![string equal [lindex $cc 0] [lindex $tt 0]]} {
# not on *n*x then
return -code error "$targetfile not on same volume as $currentpath"
}
while {[string equal [lindex $cc 0] [lindex $tt 0]] && [llength $cc] > 0} {
# discard matching components from the front
set cc [lreplace $cc 0 0]
set tt [lreplace $tt 0 0]
}
set prefix ""{}
if {[llength $cc] == 0} {
# just the file name, so targetfile is lower down (or in same place)
set prefix "."
}
# step up the tree
for {set i 0} {$i < [llength $cc]} {incr i} {
append prefix "{ .."}
}
# stick it all together (the eval is to flatten the targetfile list)
return [eval file join {*}$prefix {*}$tt]
}
======
** Page Authors **
[PYK] 2021-01-04: Fixed a typo, reworded some content, reformatted source code and page markup.
<<categories>> File