确定路径是否在Go中的另一个路径内

时间:2017-06-07 08:07:47

标签: file go path

我想删除文件的所有路径组件,但不包括整个基本目录。

实施例: /总/ BASEDIR / A / B / C /文件

我想删除"文件"然后删除" c"," b"然后" a"如果可能(目录不为空)。我想取消联系" basedir"或"整体"。

filepath.HasPrefix似乎是一个不错的选择,但它显然已被弃用:https://golang.org/pkg/path/filepath/#HasPrefix

我现在拥有的是:

p := THEPATH

// attempt to remove file and all parent directories up to the basedir
// FIXME: HasPrefix is apparently bad.. a better idea?
for filepath.HasPrefix(p, baseDir) {
    err := os.Remove(p)
    if err != nil {
        break
    }
    // climb up one
    p = filepath.Dir(p)
}

寻找一种适用于所有Go支持平台的简洁可靠的方法。

1 个答案:

答案 0 :(得分:1)

恕我直言,如果您想支持golang支持的所有平台,则路径处理相当复杂。贝娄是我迄今为止实施的解决方案(可能不是最简单的解决方案)。注意:

  1. 它支持广义操作,而不仅仅支持os.Remove
  2. 函数os.SameFile用于测试两个文件/目录是否相等,而不是基于字符串的路径比较。
  3. 在实现中,首先访问所有候选路径并将其添加到visitedPaths切片。然后,如果没有发生错误,则对每个候选路径执行action
  4. The code

    package pathwalker
    
    import (
        "os"
        "path/filepath"
        "strings"
    )
    
    type PathAction func(PathInfo) error
    type PathInfo struct {
        FileInfo os.FileInfo
        FullPath string
    }
    type PathWalker struct {
        pathName     string
        basePath     string
        visitedPaths []PathInfo
        lastFi       os.FileInfo
    }
    
    //NewPathWalker creates PathWalker instance
    func NewPathWalker(pathName, basePath string) *PathWalker {
        return &PathWalker{
            pathName: pathName,
            basePath: basePath,
        }
    }
    
    func (w *PathWalker) visit() (bool, error) {
        //Make sure path ends with separator
        basePath := filepath.Clean(w.basePath + string(filepath.Separator))
        baseInfo, err := os.Lstat(basePath)
        if err != nil {
            return false, err
        }
    
        //clean path name
        fi, err := os.Lstat(w.pathName)
        if err != nil {
            return false, err
        } else if fi.IsDir() {
            //When pathname is a directory, remove latest separator
            sep := string(filepath.Separator)
            cleanPath := filepath.Clean(w.pathName + sep)
            w.pathName = strings.TrimRight(cleanPath, sep)
        } else {
            w.pathName = filepath.Clean(w.pathName)
        }
        return w.doVisit(w.pathName, baseInfo)
    }
    
    //visit path recursively
    func (w *PathWalker) doVisit(pathName string, baseInfo os.FileInfo) (bool, error) {
        //Get file info
        fi, err := os.Lstat(pathName)
        if err != nil {
            return false, err
        }
    
        //Stop when basePath equal to pathName
        if os.SameFile(fi, baseInfo) {
            return true, nil
        }
    
        //Top directory reached, but does not match baseInfo
        if w.lastFi != nil && os.SameFile(w.lastFi, fi) {
            return false, nil
        }
        w.lastFi = fi
    
        //Append to visited path list
        w.visitedPaths = append(w.visitedPaths, PathInfo{fi, pathName})
    
        //Move to upper path
        up := filepath.Dir(pathName)
        if up == "." {
            return false, nil
        }
    
        //Visit upper directory
        return w.doVisit(up, baseInfo)
    }
    
    //Walk perform action then return number of proceed paths and error
    func (w *PathWalker) Walk(act PathAction) (int, error) {
        n := 0
        ok, err := w.visit()
        if err != nil {
            return 0, err
        } else if ok && act != nil {
            for _, pi := range w.visitedPaths {
                err := act(pi)
                if err != nil {
                    return n, err
                }
                n++
            }
        }
        return n, nil
    }
    
    //VisitedPaths return list of visited paths
    func (w *PathWalker) VisitedPaths() []PathInfo {
        return w.visitedPaths
    }
    

    然后,如果要删除basePath下的文件和父目录,可以执行以下操作:

    func remove(pathName, basePath string) {
        act := func(p pathwalker.PathInfo) error {
            if p.FileInfo.IsDir() {
                fmt.Printf("  Removing directory=%s\n", p.FullPath)
                return os.Remove(p.FullPath)
            }
    
            fmt.Printf("  Removing file=%s\n", p.FullPath)
            return os.Remove(p.FullPath)
        }
    
        pw := pathwalker.NewPathWalker(pathName, basePath)
        n, err := pw.Walk(act)
        fmt.Printf("Removed: %d/%d, err=%v\n", n, len(pw.VisitedPaths()), err)
    }
    

    如果您只想测试路径是否在另一条路径内,您可以执行以下操作:

    n, err := pathwalker.NewPathWalker(fileName, basePath).Walk(nil)
    if n > 0 && err != nil {
        //is inside another path
    }