'check if given path is a subdirectory of another in golang
Say we have two paths:
c:\foo\bar\baz
and c:\foo\bar
Is there any package/method that will help me determine if one is a subdirectory of another? I am looking at a cross-platform option.
Solution 1:[1]
You could try and use path.filepath.Rel():
func Rel(basepath, targpath string) (string, error)
Rel
returns a relative path that is lexically equivalent totargpath
when joined tobasepath
with an intervening separator.
That is,Join(basepath, Rel(basepath, targpath))
is equivalent totargpath
itself
That means Rel("c:\foo\bar", "c:\foo\bar\baz")
should be baz
, meaning a subpath completely included in c:\foo\bar\baz
, and without any '../
'.
The same would apply for unix paths.
That would make c:\foo\bar\baz
a subdirectory of c:\foo\bar
.
Solution 2:[2]
You can use the function path.filepath.Match()
Match reports whether name matches the shell file name pattern.
For example:
pattern := "C:\foo\bar" + string(filepath.Separator) + "*"
matched, err := filepath.Match(pattern, "C:\foo\bar\baz")
Where matched
should be true
.
Solution 3:[3]
I haven't found a reliable solution for all types of paths, but the best you can get is by using filepath.Rel
as VonC suggested.
It works if both filepaths are either absolute or relative (mixing is not allowed) and works on both Windows and Linux:
func SubElem(parent, sub string) (bool, error) {
up := ".." + string(os.PathSeparator)
// path-comparisons using filepath.Abs don't work reliably according to docs (no unique representation).
rel, err := filepath.Rel(parent, sub)
if err != nil {
return false, err
}
if !strings.HasPrefix(rel, up) && rel != ".." {
return true, nil
}
return false, nil
}
Absolute windows paths that start with a drive letter will require an additional check though.
Solution 4:[4]
Try this code. This checks if either is a sub-directory of the other. Try changing values of both base and path and the results should be valid.
package main
import (
"fmt"
"path/filepath"
"strings"
)
func main() {
base := "/b/c/"
path := "/a/b/c/d"
if len(base) > len(path) {
base, path = path, base
}
rel, err := filepath.Rel(base, path)
fmt.Printf("Base %q: Path %q: Rel %q Err %v\n", base, path, rel, err)
if err != nil {
fmt.Println("PROCEED")
return
}
if strings.Contains(rel, "..") {
fmt.Println("PROCEED")
return
}
fmt.Println("DENY")
}
Solution 5:[5]
If you first canonicalize both paths by calling filepath.EvalSymlinks()
and filepath.Abs()
on them, you can simply append a '/' to each one, since the UNIX kernel itself forbids a '/' within a path component. At this point you can simply use strings.HasPrefix()
on the two paths, in either order.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | VonC |
Solution 2 | Oleg Burov |
Solution 3 | maja |
Solution 4 | dpaks |
Solution 5 | AbuNassar |