英文:
check if given path is a subdirectory of another in golang
问题
我们有两个路径:
c:\foo\bar\baz
和 c:\foo\bar
有没有任何包/方法可以帮助我确定一个路径是否是另一个路径的子目录?我正在寻找一个跨平台的选项。
英文:
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.
答案1
得分: 16
你可以尝试使用path/filepath.Rel()函数:
func Rel(basepath, targpath string) (string, error)
Rel
函数返回一个相对路径,当与一个中间分隔符连接到basepath
时,该路径在语义上等同于targpath
。也就是说,Join(basepath, Rel(basepath, targpath))
等同于targpath
本身。
这意味着Rel("c:\foo\bar", "c:\foo\bar\baz")
应该返回baz
,表示一个完全包含在c:\foo\bar\baz
中的子路径,而且没有任何../
。对于Unix路径也是一样的。
这将使c:\foo\bar\baz
成为c:\foo\bar
的子目录。
英文:
You could try and use path.filepath.Rel():
func Rel(basepath, targpath string) (string, error)
> Rel
returns a relative path that is lexically equivalent to targpath
when joined to basepath
with an intervening separator.
That is, Join(basepath, Rel(basepath, targpath))
is equivalent to targpath
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
.
答案2
得分: 5
我还没有找到适用于所有路径类型的可靠解决方案,但你可以按照VonC的建议使用filepath.Rel
来获得最佳解决方案。
如果两个文件路径都是绝对路径或相对路径(不允许混合使用),并且适用于Windows和Linux:
func SubElem(parent, sub string) (bool, error) {
up := ".." + string(os.PathSeparator)
// 根据文档,使用filepath.Abs进行路径比较不可靠(没有唯一表示)。
rel, err := filepath.Rel(parent, sub)
if err != nil {
return false, err
}
if !strings.HasPrefix(rel, up) && rel != ".." {
return true, nil
}
return false, nil
}
但是,以驱动器字母开头的绝对Windows路径需要进行额外的检查。
英文:
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.
答案3
得分: 4
你可以使用函数path.filepath.Match()。
Match函数用于判断name是否与shell文件名模式匹配。
例如:
pattern := "C:\\foo\\bar" + string(filepath.Separator) + "*"
matched, err := filepath.Match(pattern, "C:\\foo\\bar\\baz")
其中matched
应该是true
。
英文:
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
.
答案4
得分: 1
如果你首先通过调用filepath.EvalSymlinks()
和filepath.Abs()
对两个路径进行规范化,然后可以简单地在每个路径后面添加'/'
,因为UNIX内核本身禁止在路径组件中使用'/'
。此时,你可以以任意顺序使用strings.HasPrefix()
来比较这两个路径。
英文:
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.
答案5
得分: -1
尝试这段代码。它会检查其中一个路径是否是另一个路径的子目录。尝试更改base和path的值,结果应该是有效的。
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")
}
英文:
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")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论