英文:
Determine if path is inside another path in Go
问题
我想删除文件的所有路径组件,直到(但不包括)整体基本目录。
示例:
/overall/basedir/a/b/c/file
我想删除"file",然后删除"c","b",然后如果可能的话删除"a"(目录不为空)。我不想删除"basedir"或"overall"。
filepath.HasPrefix似乎是一个不错的选择,但它显然已被弃用:https://golang.org/pkg/path/filepath/#HasPrefix
我现在的代码是:
p := THEPATH
// 尝试删除文件和所有父目录,直到basedir
// FIXME: HasPrefix显然不好..有更好的方法吗?
for filepath.HasPrefix(p, baseDir) {
err := os.Remove(p)
if err != nil {
break
}
// 向上爬一级
p = filepath.Dir(p)
}
寻找一种简洁可靠的方法,适用于所有Go支持的平台。
英文:
I would like to delete all path components for a file up to (but not including) an overall base directory.
Example:
/overall/basedir/a/b/c/file
I want to remove "file" and then remove "c", "b" and then "a" if possible (directories not empty). I do not want to unlink "basedir" or "overall".
filepath.HasPrefix would seem to be a good option but it's apparently deprecated: https://golang.org/pkg/path/filepath/#HasPrefix
What I have now is:
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)
}
Looking for a succinct and reliable way that works on all Go supported platforms.
答案1
得分: 1
在我看来,如果你想要支持golang支持的所有平台,路径处理会相当复杂。下面是我迄今为止实现的解决方案(可能不是最简单的解决方案)。注意:
- 它支持通用的操作,而不仅仅是
os.Remove。 - 使用函数
os.SameFile而不是基于字符串的路径比较,用于测试两个文件/目录是否相等。 - 在实现中,首先访问并将所有候选路径添加到
visitedPaths切片中。然后,如果没有错误发生,对每个候选路径执行一个action。
以下是代码:
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
}
英文:
IMHO, path handling is rather complicated if you want to support all platforms that is supported by golang. Bellow is the solution that I've implemented so far (probably not the simplest one). Notes:
- It supports generalized action rather than only
os.Remove - Instead of string-based path comparison, function
os.SameFileis used to test whether two files/directories are equal. - In the implementation, at first all candidate paths are visited and added to
visitedPathsslice. Then, if no error occurs, anactionis perform to each candidate path.
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
}
Then if you want to remove file and parent directory under basePath, you can do:
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)
}
If you just want to test whether a path is inside another path, you can do:
n, err := pathwalker.NewPathWalker(fileName, basePath).Walk(nil)
if n > 0 && err != nil {
//is inside another path
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论