infinite-blank-
infinite-blank-

Reputation: 177

How to set depth for recursive iteration of directories in filepath.walk() func in Golang?

I want to search for a specific type of file inside a directory which has various sub-directories in it. I'm using filepath.walk() in Golang for this. However, I don't want to iterate recursively beyond a max depth where I know that file can't exist.

Is there any such pre-built function/library in Golang?

Upvotes: 4

Views: 5292

Answers (2)

Spouk
Spouk

Reputation: 712

func ControlDeepWalk(basepath string, count int, hard, debug bool) error {
    var (
        stock       = make(map[string][]string)
        countBase   = 0
        correctdirs []string
    )
    base := filepath.Base(basepath)
    dirbase := filepath.Dir(basepath)
    countBase = len(strings.Split(filepath.Dir(basepath), "/"))
    if debug {
        log.Printf("countbase: %v  %v\n", strings.Split(filepath.Dir(basepath), "/"), countBase)
        log.Printf("base :%v : %v : %v\n", base, dirbase, strings.Split(basepath, "/"))
    }

    err := filepath.WalkDir(basepath, func(path string, d fs.DirEntry, err error) error {
        if err != nil {
            if debug {
                log.Printf("--error inf walkdir function, exit")
            }
            return err
        }
        if d.IsDir() {
            if filepath.Dir(path) == filepath.Dir(basepath) {
                if debug {
                    log.Printf("found root directory, skipping  %v :  %v\n", filepath.Dir(path), filepath.Dir(basepath))
                }
            } else {
                compare := false
                if hard {
                    compare = len(strings.Split(filepath.Dir(path), "/")) == countBase+count
                } else {
                    compare = len(strings.Split(filepath.Dir(path), "/")) <= countBase+count
                }
                if compare {
                    if debug {
                        log.Printf("-found dir: [%v] %v\n", path, d.Name())
                    }
                    stock[filepath.Dir(filepath.Join(path, d.Name()))] = []string{}
                    correctdirs = append(correctdirs, filepath.Dir(filepath.Join(path, d.Name())))
                }
            }
        } else {
            fdir, ffile := filepath.Split(path)
            for _, x := range correctdirs {
                if x == filepath.Dir(fdir) {
                    if debug {
                        log.Printf("-found file:%v  : %v   %v  %v \n", d.Name(), path, fdir, ffile)
                    }
                    stock[x] = append(stock[x], d.Name())
                }
            }
        }
        return nil
    })
    if debug {
        for k, v := range stock {
            log.Printf("%v : %v \n", k, v)
        }
        log.Printf("%v\n", stock)
    }
    return err
}



func main() {
    p := "/backup/backuper/test"
    count := 2
    _ = ControlDeepWalk(p, count, false, true)

}

enjoy, dude! :)

Upvotes: -1

blackgreen
blackgreen

Reputation: 44787

First, you should use filepath.WalkDir introduced in Go 1.16, which is more efficient than filepath.Walk.

Walk is less efficient than WalkDir, introduced in Go 1.16, which avoids calling os.Lstat on every visited file or directory.

Then, there is no way to specify the max depth as a direct argument. You have to compute the recursion depth in the WalkDirFunc.

Apparently counting separators in the filepath is an acceptable strategy (and arguably simpler than other possible tricks), so the solution might look like:

    maxDepth := 2
    rootDir := "root"
    err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
        if err != nil {
            // handle possible path err, just in case...
            return err
        }
        if d.IsDir() && strings.Count(path, string(os.PathSeparator)) > maxDepth {
            fmt.Println("skip", path)
            return fs.SkipDir
        }
        // ... process entry
        return nil
    })

So with dir structure as the following:

.
└── root
    ├── a.txt
    ├── b.txt
    └── root1
        ├── a.txt
        └── root2
            ├── a.txt
            ├── b.txt
            ├── root3
            │   └── a.txt
            └── root4

and assuming root is at depth 0, the above code the above code prints:

skip root/root1/root2/root3
skip root/root1/root2/root4

Upvotes: 7

Related Questions