检查给定路径是否是Go语言中另一个路径的子目录。

huangapple go评论80阅读模式
英文:

check if given path is a subdirectory of another in golang

问题

我们有两个路径:

c:\foo\bar\bazc:\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")
}

huangapple
  • 本文由 发表于 2015年1月19日 20:31:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/28024731.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定