### Install doublestar Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Install the doublestar package using go get. This command fetches the latest version of the v4 package. ```bash go get github.com/bmatcuk/doublestar/v4 ``` -------------------------------- ### Example of SplitPattern Usage Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Demonstrates how to use SplitPattern to prepare a pattern for os.DirFS and Glob. It's the user's responsibility to ensure the returned base path is safe. ```go base, pattern := SplitPattern("../../path/to/meta*/**") fsys := os.DirFS(base) matches, err := Glob(fsys, pattern) ``` -------------------------------- ### Recursive FilepathGlob Example Source: https://context7.com/bmatcuk/doublestar/llms.txt Demonstrates a simple recursive glob on the real filesystem. Paths are returned using the OS-specific path separator. ```go package main import ( "fmt" "github.com/bmatcuk/doublestar/v4" ) func main() { // Example 1: simple recursive glob on real filesystem matches, err := doublestar.FilepathGlob("../../myproject/**/*.go") if err != nil { fmt.Println("error:", err) return } for _, m := range matches { fmt.Println(m) // paths use OS separator (\ on Windows) } // Example 2: case-insensitive glob (useful on Windows or macOS) matches2, err := doublestar.FilepathGlob("/data/reports/**/*.CSV", doublestar.WithCaseInsensitive(), doublestar.WithFilesOnly(), ) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d CSV files\n", len(matches2)) // Example 3: no-symlink-follow with files only matches3, err := doublestar.FilepathGlob("/srv/data/**/*.json", doublestar.WithNoFollow(), doublestar.WithFilesOnly(), ) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d json files (no symlinks followed)\n", len(matches3)) } ``` -------------------------------- ### Doublestar Pattern Syntax Examples Source: https://context7.com/bmatcuk/doublestar/llms.txt Demonstrates various pattern syntax elements supported by doublestar, including wildcards, character classes, alternatives, and escaped meta characters. ```go package main import ( "fmt" "testing/fstest" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := fstest.MapFS{ "a/b/c/file.go": {}, "a/b/file.go": {}, "a/file.go": {}, "a/README.md": {}, "b/other.go": {}, "docs/v1/index.md": {}, "docs/v2/index.md": {}, } examples := []struct { desc, pattern string }{ {"star: single segment wildcard", "a/*.go"}, {"doublestar: recursive wildcard", "a/**/*.go"}, {"doublestar matches zero dirs", "a/**"}, {"?: single char (non-separator)", "a/b/c/fil?.go"}, {"[class]: character class", "docs/v[12]/index.md"}, {"[range]: character range", "docs/v[0-9]/index.md"}, {"[^negate]: negated class", "docs/v[^3-9]/index.md"}, {"{alts}: alternatives", "{a,b}/*.go"}, {"nested alts", "{a,docs/v{1,2}}/**"}, {"escaped meta: literal *", `a/\*.go`}, // matches file literally named *.go } for _, ex := range examples { matches, err := doublestar.Glob(fsys, ex.pattern) if err != nil { fmt.Printf("% -40s ERROR: %v\n", ex.desc, err) continue } fmt.Printf("% -40s %v\n", ex.desc, matches) } } ``` -------------------------------- ### Glob: File System Globbing with Extended Patterns Source: https://context7.com/bmatcuk/doublestar/llms.txt Glob returns all file names in an fs.FS matching the pattern, supporting '**' and '{alts}'. Patterns must use '/'. It ignores I/O errors by default; use WithFailOnIOErrors to change this. Patterns with './', '../', or starting with '/' yield no results. ```go package main import ( "fmt" "os" "github.com/bmatcuk/doublestar/v4" ) func main() { // Example 1: basic glob on os.DirFS fsys := os.DirFS("/usr/local") matches, err := doublestar.Glob(fsys, "**/*.go") if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d .go files under /usr/local\n", len(matches)) // Example 2: alternatives pattern - match multiple extensions fsys2 := os.DirFS(".") matches2, err := doublestar.Glob(fsys2, "**/*.{go,mod,sum}", doublestar.WithFilesOnly(), // skip directories doublestar.WithNoHidden(), // skip dotfiles/dot-directories ) if err != nil { fmt.Println("error:", err) return } for _, m := range matches2 { fmt.Println(m) } // Example 3: only return directories matching pattern matches3, _ := doublestar.Glob(os.DirFS("."), "**/testdata/") for _, m := range matches3 { fmt.Println("dir:", m) } // Example 4: fail on I/O errors and on non-existent base path _, err = doublestar.Glob(os.DirFS("."), "nonexistent/**", doublestar.WithFailOnIOErrors(), doublestar.WithFailOnPatternNotExist(), ) if err != nil { fmt.Println("expected error:", err) // doublestar.ErrPatternNotExist } } ``` -------------------------------- ### Disable Matching Hidden Files/Directories Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Use WithNoHidden to exclude hidden files and directories (those starting with a dot) from wildcard matches, mimicking traditional shell behavior. Hidden files can still be matched with explicit patterns like ".*" or ".config/**". ```go WithNoHidden() ``` -------------------------------- ### Glob with NoHidden Option Source: https://context7.com/bmatcuk/doublestar/llms.txt Use WithNoHidden to exclude hidden files and directories (those starting with '.') from glob results, unless explicitly matched. On Windows, this checks file attributes. ```go package main import ( "fmt" "testing/fstest" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := fstest.MapFS{ "main.go": {}, ".env": {}, ".git/config": {}, "src/util.go": {}, "src/.hidden.go": {}, } // Without WithNoHidden: matches everything including dotfiles all, _ := doublestar.Glob(fsys, "**/*.go") fmt.Println("including hidden:", all) // [.git/config src/.hidden.go src/util.go] (note: .git/config won't match *.go) // With WithNoHidden: skips .hidden.go and does not descend into .git/ visible, _ := doublestar.Glob(fsys, "**/*.go", doublestar.WithNoHidden()) fmt.Println("no hidden:", visible) // [main.go src/util.go] // Explicitly targeting hidden files still works hidden, _ := doublestar.Glob(fsys, ".*") fmt.Println("explicit hidden:", hidden) // [.env] } ``` -------------------------------- ### GlobWalk: Efficient File Traversal with Callback Source: https://context7.com/bmatcuk/doublestar/llms.txt GlobWalk calls a callback for each matching file, avoiding result slice allocations and providing fs.DirEntry metadata. The callback can return doublestar.SkipDir to skip directories or any error to abort. ```go package main import ( "fmt" "io/fs" "os" "regexp" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := os.DirFS(".") // Example 1: collect large Go files (> 10KB) without building full list var largeFiles []string err := doublestar.GlobWalk(fsys, "**/*.go", func(path string, d fs.DirEntry) error { info, err := d.Info() if err != nil { return err } if info.Size() > 10*1024 { largeFiles = append(largeFiles, path) } return nil }) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Large .go files: %v\n", largeFiles) // Example 2: two-stage filtering (glob + regex) — skip whole dirs early re := regexp.MustCompile(`_v\d+\.go$`) err = doublestar.GlobWalk(fsys, "**/*.go", func(path string, d fs.DirEntry) error { if d.IsDir() { if d.Name() == "vendor" { return doublestar.SkipDir // skip vendor entirely } return nil } if re.MatchString(path) { fmt.Println("versioned file:", path) } return nil }, doublestar.WithNoHidden()) // Example 3: early exit on first match found := "" _ = doublestar.GlobWalk(fsys, "**/go.mod", func(path string, d fs.DirEntry) error { found = path return fmt.Errorf("stop") // any non-SkipDir error aborts walk }) if found != "" { fmt.Println("first go.mod:", found) } } ``` -------------------------------- ### OS-Native Path Matching Source: https://context7.com/bmatcuk/doublestar/llms.txt Use PathMatch for patterns and names that use OS-native path separators (e.g., '\' on Windows). It behaves identically to Match() on Unix-like systems. ```go package main import ( "fmt" "path/filepath" "github.com/bmatcuk/doublestar/v4" ) func main() { // On Windows, patterns and names can use native backslash separators. // On Unix, this behaves identically to Match(). pattern := filepath.FromSlash("src/**/*.go") name := filepath.FromSlash("src/internal/util.go") matched, err := doublestar.PathMatch(pattern, name) if err != nil { fmt.Println("bad pattern:", err) return } fmt.Printf("PathMatch(%q, %q) = %v\n", pattern, name, matched) } ``` -------------------------------- ### Walk Directory with Glob Pattern Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Calls a callback function for every file matching a pattern. Avoids allocating memory for matches and provides access to fs.DirEntry objects. Can quit early by returning a non-nil error from the callback. Use WithFailOnIOErrors to abort on I/O errors. ```go type GlobWalkFunc func(path string, d fs.DirEntry) error func GlobWalk(fsys fs.FS, pattern string, fn GlobWalkFunc, opts ...GlobOption) error ``` -------------------------------- ### SplitPattern Usage for Safe Globbing Source: https://context7.com/bmatcuk/doublestar/llms.txt Splits a pattern into a static base path and a glob pattern, essential for using `Glob` with `os.DirFS` safely. The base path is unescaped for direct use with `os.DirFS`. ```go package main import ( "fmt" "os" "github.com/bmatcuk/doublestar/v4" ) func main() { patterns := []string{ "../../path/to/meta*/**", "/home/user/projects/**/*.go", "meta*/**", // no static prefix "src/cmd/server.go", // no meta chars at all } for _, p := range patterns { base, pattern := doublestar.SplitPattern(p) fmt.Printf("SplitPattern(%q)\n base=%q pattern=%q\n", p, base, pattern) } // Output: // SplitPattern("../../path/to/meta*/**") // base="../../path/to" pattern="meta*/**" // SplitPattern("/home/user/projects/**/*.go") // base="/home/user/projects" pattern="**/*.go" // SplitPattern("meta*/**") // base="." pattern="meta*/**" // SplitPattern("src/cmd/server.go") // base="src/cmd" pattern="server.go" // Typical usage: handle relative patterns safely base, pat := doublestar.SplitPattern("../../configs/**/*.yaml") fsys := os.DirFS(base) matches, err := doublestar.Glob(fsys, pat) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d YAML files\n", len(matches)) } ``` -------------------------------- ### Enable IO Error Handling for Globbing Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Pass WithFailOnIOErrors to make glob functions return immediately with an error if any I/O operation fails during the filesystem traversal. By default, I/O errors are ignored. ```go WithFailOnIOErrors() ``` -------------------------------- ### Glob with FailOnIOErrors Option Source: https://context7.com/bmatcuk/doublestar/llms.txt Enable WithFailOnIOErrors to make glob functions return I/O errors encountered during filesystem traversal, instead of silently ignoring them. This differs from patterns not existing, which require WithFailOnPatternNotExist. ```go package main import ( "fmt" "os" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := os.DirFS("/restricted") // Default: I/O errors (e.g., permission denied) are silently ignored matches, err := doublestar.Glob(fsys, "**/*.conf") fmt.Println("silently skipped errors, matches:", len(matches), "err:", err) // With WithFailOnIOErrors: permission errors abort and are returned matches2, err2 := doublestar.Glob(fsys, "**/*.conf", doublestar.WithFailOnIOErrors()) if err2 != nil { fmt.Println("aborted on I/O error:", err2) return } fmt.Println("matches:", matches2) } ``` -------------------------------- ### Split Pattern into Base and Pattern Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Splits a pattern into a base path and a pattern string. Useful for initializing os.DirFS() before calling Glob(). Returns '.' and the unaltered pattern if no suitable split point is found. ```go func SplitPattern(p string) (base, pattern string) ``` -------------------------------- ### GlobWalk Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Calls the callback function `fn` for every file matching the pattern. It offers a performance benefit over `Glob` by avoiding slice allocation and provides access to `fs.DirEntry` objects. The walk can be terminated early by returning a non-nil error from the callback. The `WithFailOnIOErrors` option can be used to abort on I/O errors. ```APIDOC ## GlobWalk ### Description Calls the callback function `fn` for every file matching pattern. The syntax of pattern is the same as in Match() and the behavior is the same as Glob(), with regard to limitations (such as patterns containing `/./`, `/../`, or starting with `/`). The pattern may describe hierarchical names such as usr/*/bin/ed. GlobWalk may have a small performance benefit over Glob if you do not need a slice of matches because it can avoid allocating memory for the matches. Additionally, GlobWalk gives you access to the `fs.DirEntry` objects for each match, and lets you quit early by returning a non-nil error from your callback function. Like `io/fs.WalkDir`, if your callback returns `SkipDir`, GlobWalk will skip the current directory. This means that if the current path _is_ a directory, GlobWalk will not recurse into it. If the current path is not a directory, the rest of the parent directory will be skipped. GlobWalk ignores file system errors such as I/O errors reading directories by default. GlobWalk may return `ErrBadPattern`, reporting that the pattern is malformed. To enable aborting on I/O errors, the `WithFailOnIOErrors` option can be passed. Additionally, if the callback function `fn` returns an error, GlobWalk will exit immediately and return that error. Like Glob(), this function assumes that your pattern uses `/` as the path separator even if that's not correct for your OS (like Windows). If you aren't sure if that's the case, you can use filepath.ToSlash() on your pattern before calling GlobWalk(). Note: users should _not_ count on the returned error, `doublestar.ErrBadPattern`, being equal to `path.ErrBadPattern`. ### Signature ```go type GlobWalkFunc func(path string, d fs.DirEntry) error func GlobWalk(fsys fs.FS, pattern string, fn GlobWalkFunc, opts ...GlobOption) error ``` ``` -------------------------------- ### Enable Case-Insensitive Globbing Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Use WithCaseInsensitive to make glob patterns match files and directories regardless of case. This is particularly useful on case-insensitive file systems like Windows. ```go WithCaseInsensitive() ``` -------------------------------- ### PathMatch Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Similar to `Match`, but uses the system's path separator to split `name` and `pattern`. It assumes both inputs use the system's separator. ```APIDOC ## PathMatch ```go func PathMatch(pattern, name string) (bool, error) ``` ### Description PathMatch returns true if `name` matches the file name `pattern` ([see "patterns"]). The difference between Match and PathMatch is that PathMatch will automatically use your system's path separator to split `name` and `pattern`. On systems where the path separator is `'\'`, escaping will be disabled. Note: this is meant as a drop-in replacement for `filepath.Match()`. It assumes that both `pattern` and `name` are using the system's path separator. If you can't be sure of that, use `filepath.ToSlash()` on both `pattern` and `name`, and then use the `Match()` function instead. ``` -------------------------------- ### Perform Glob Search for Matching File Paths Source: https://github.com/bmatcuk/doublestar/blob/master/README.md The Glob function returns a list of file paths matching the provided pattern. It ignores I/O errors by default but can be configured to report them using WithFailOnIOErrors. Patterns use '/' as a separator. ```go func Glob(fsys fs.FS, pattern string, opts ...GlobOption) ([]string, error) ``` -------------------------------- ### Glob with FailOnPatternNotExist Option Source: https://context7.com/bmatcuk/doublestar/llms.txt Illustrates how WithFailOnPatternNotExist causes Glob to return ErrPatternNotExist for non-existent base paths. Alternatives are expanded before this check. ```go package main import ( "errors" "fmt" "os" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := os.DirFS(".") // Default: non-existent base paths silently return no matches matches, err := doublestar.Glob(fsys, "nonexistent/path/**/*.go") fmt.Println("default:", matches, err) // [] // WithFailOnPatternNotExist: returns ErrPatternNotExist _, err2 := doublestar.Glob(fsys, "nonexistent/path/**/*.go", doublestar.WithFailOnPatternNotExist(), ) if errors.Is(err2, doublestar.ErrPatternNotExist) { fmt.Println("pattern base path does not exist") // printed } // Alternatives are expanded first: fails if either "a" or "b" don't exist _, err3 := doublestar.Glob(fsys, "{missing_dir,also_missing}/*.go", doublestar.WithFailOnPatternNotExist(), ) fmt.Println("alt error:", err3) } ``` -------------------------------- ### Glob with FilesOnly Option Source: https://context7.com/bmatcuk/doublestar/llms.txt Employ WithFilesOnly to ensure that globbing results include only non-directory files. Directory entries are excluded by default. ```go package main import ( "fmt" "testing/fstest" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := fstest.MapFS{ "src/main.go": {}, "src/pkg/util.go": {}, "src/pkg/": {Mode: 0o755 | 1<<31}, // directory entry } // Without option: returns both files and directories all, _ := doublestar.Glob(fsys, "src/**") fmt.Println("all:", all) // With WithFilesOnly: directories excluded filesOnly, _ := doublestar.Glob(fsys, "src/**", doublestar.WithFilesOnly()) fmt.Println("files only:", filesOnly) } ``` -------------------------------- ### Match string against pattern Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Use Match to check if a given name string conforms to a pattern. This function splits both pattern and name by '/' and is suitable for absolute or relative paths. It returns an error only if the pattern is malformed. ```go func Match(pattern, name string) (bool, error) ``` -------------------------------- ### Fast Path Matching with Pre-validation Source: https://context7.com/bmatcuk/doublestar/llms.txt Use MatchUnvalidated for faster matching after pre-validating the pattern with ValidatePattern. This is beneficial when matching many paths against the same pattern. ```go package main import ( "fmt" "github.com/bmatcuk/doublestar/v4" ) func main() { pattern := "src/**/*.go" // Pre-validate once, then match many paths cheaply if !doublestar.ValidatePattern(pattern) { fmt.Println("invalid pattern") return } paths := []string{ "src/main.go", "src/pkg/util.go", "src/pkg/deep/nested/file.go", "tests/main_test.go", } for _, p := range paths { if doublestar.MatchUnvalidated(pattern, p) { fmt.Println("matched:", p) } } } ``` -------------------------------- ### GlobWalk for Custom Filtering Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Use GlobWalk for a two-stage filtering process when globs are insufficient. The pattern argument performs an initial pass, and the provided function handles custom filtering or directory skipping. ```go var matches []string err := doublestar.GlobWalk(fsys, pattern, func(p string, d fs.DirEntry) error { if (customFilter(p, d)) { matches = append(matches, p) } else if (d.isDir()) { return doublestar.SkipDir } return nil }) return matches, err ``` -------------------------------- ### PathMatch Source: https://context7.com/bmatcuk/doublestar/llms.txt The drop-in replacement for filepath.Match. It automatically uses the OS path separator to split both pattern and name, making it correct on Windows where paths use '\'. ```APIDOC ## PathMatch ### Description `PathMatch` is the drop-in replacement for `filepath.Match`. It automatically uses the OS path separator to split both pattern and name, making it correct on Windows where paths use `\`. On Windows, escaping via `\` is disabled since `\` is the path separator. ### Method `PathMatch(pattern string, name string) (matched bool, err error)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go package main import ( "fmt" "path/filepath" "github.com/bmatcuk/doublestar/v4" ) func main() { // On Windows, patterns and names can use native backslash separators. // On Unix, this behaves identically to Match(). pattern := filepath.FromSlash("src/**/*.go") name := filepath.FromSlash("src/internal/util.go") matched, err := doublestar.PathMatch(pattern, name) if err != nil { fmt.Println("bad pattern:", err) return } fmt.Printf("PathMatch(%q, %q) = %v\n", pattern, name, matched) } ``` ### Response #### Success Response (200) - **matched** (bool) - True if the path matches the pattern, false otherwise. - **err** (error) - An error if the pattern is malformed, nil otherwise. #### Response Example ``` PathMatch("src/**/*.go", "src/internal/util.go") = true ``` ``` -------------------------------- ### Enable Pattern Not Found Error Handling Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Use WithFailOnPatternNotExist to cause glob functions to return an error if the pattern references a path that does not exist before any wildcards are processed. This check happens before meta-character expansion. ```go WithFailOnPatternNotExist() ``` -------------------------------- ### Filepath Globbing Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Returns names of all files matching a pattern or nil if no match. Ignores file system errors by default. Use WithFailOnIOErrors to abort on I/O errors. This is a convenience function for users who don't need io/fs. ```go func FilepathGlob(pattern string, opts ...GlobOption) (matches []string, err error) ``` -------------------------------- ### ValidatePathPattern Function Signature Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Use ValidatePathPattern for OS-specific path matching, similar to filepath.Match(). For general globbing, use ValidatePattern. ```go func ValidatePathPattern(s string) bool ``` -------------------------------- ### Glob Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Returns the names of all files matching a pattern or nil if there is no matching file. ```APIDOC ## Glob ```go func Glob(fsys fs.FS, pattern string, opts ...GlobOption) ([]string, error) ``` Glob returns the names of all files matching pattern or nil if there is no matching file. The syntax of patterns is the same as in `Match()`. The pattern may describe hierarchical names such as `usr/*/bin/ed`. Glob ignores file system errors such as I/O errors reading directories by default. The only possible returned error is `ErrBadPattern`, reporting that the pattern is malformed. To enable aborting on I/O errors, the `WithFailOnIOErrors` option can be passed. Note: this is meant as a drop-in replacement for `io/fs.Glob()`. Like `io/fs.Glob()`, this function assumes that your pattern uses `/` as the path separator even if that's not correct for your OS (like Windows). If you aren't sure if that's the case, you can use `filepath.ToSlash()` on your pattern before calling `Glob()`. Like `io/fs.Glob()`, patterns containing `/./`, `/../`, or starting with `/` will return no results and no errors. This seems to be a [conscious decision](https://github.com/golang/go/issues/44092#issuecomment-774132549), even if counter-intuitive. You can use [SplitPattern] to divide a pattern into a base path (to initialize an `FS` object) and pattern. Note: users should _not_ count on the returned error, `doublestar.ErrBadPattern`, being equal to `path.ErrBadPattern`. ``` -------------------------------- ### GlobWalk Source: https://context7.com/bmatcuk/doublestar/llms.txt Calls a callback function for every file matching the pattern instead of collecting results into a slice. This avoids memory allocations for the result slice and provides access to `fs.DirEntry` metadata. ```APIDOC ## GlobWalk ### Description Calls a callback function for every file matching the pattern instead of collecting results into a slice. This avoids memory allocations for the result slice and provides access to `fs.DirEntry` metadata. The callback can return `doublestar.SkipDir` to skip recursing into a directory, or any other error to abort early — matching `io/fs.WalkDir` semantics. ### Function Signature `doublestar.GlobWalk(fsys fs.FS, pattern string, walkFn fs.WalkDirFunc, options ...doublestar.GlobOption) error` ### Parameters - **fsys** (fs.FS) - The file system to search within. - **pattern** (string) - The glob pattern to match. - **walkFn** (fs.WalkDirFunc) - The callback function to execute for each matching file or directory. - **options** (...doublestar.GlobOption) - Optional configuration for globbing behavior (e.g., `WithNoHidden`). ### Return Value - (error) - An error if one occurs during the walk operation, or if the `walkFn` returns an error (other than `doublestar.SkipDir`). ### Example ```go import ( "fmt" "io/fs" "os" "regexp" "github.com/bmatcuk/doublestar/v4" ) func main() { fsys := os.DirFS(".") // Example 1: collect large Go files (> 10KB) without building full list var largeFiles []string err := doublestar.GlobWalk(fsys, "**/*.go", func(path string, d fs.DirEntry) error { info, err := d.Info() if err != nil { return err } if info.Size() > 10*1024 { largeFiles = append(largeFiles, path) } return nil }) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Large .go files: %v\n", largeFiles) // Example 2: two-stage filtering (glob + regex) — skip whole dirs early re := regexp.MustCompile(`_v\d+\.go$`) err = doublestar.GlobWalk(fsys, "**/*.go", func(path string, d fs.DirEntry) error { if d.IsDir() { if d.Name() == "vendor" { return doublestar.SkipDir // skip vendor entirely } return nil } if re.MatchString(path) { fmt.Println("versioned file:", path) } return nil }, doublestar.WithNoHidden()) // Example 3: early exit on first match found := "" _ = doublestar.GlobWalk(fsys, "**/go.mod", func(path string, d fs.DirEntry) error { found = path return fmt.Errorf("stop") // any non-SkipDir error aborts walk }) if found != "" { fmt.Println("first go.mod:", found) } } ``` ``` -------------------------------- ### Import doublestar in Go code Source: https://github.com/bmatcuk/doublestar/blob/master/README.md Import the doublestar package into your Go source files to use its functionalities. Ensure you are importing the correct version. ```go import "github.com/bmatcuk/doublestar/v4" ``` -------------------------------- ### SplitPattern Source: https://context7.com/bmatcuk/doublestar/llms.txt Splits a glob pattern into a static base path and the remaining glob pattern. This is useful for using `os.DirFS` with patterns that have meta characters. ```APIDOC ## SplitPattern Splits a glob pattern into a static base path (everything before the first meta character's path segment) and the remaining glob pattern. This is essential when using `Glob` with `os.DirFS` because `io/fs` silently rejects patterns containing `/../` or `/./` anywhere in the path. `SplitPattern` also unescapes meta characters in the returned base path so it can be passed directly to `os.DirFS`. ### Function Signature `SplitPattern(pattern string) (base, pattern string)` ### Parameters - `pattern` (string): The glob pattern to split. ### Returns - `base` (string): The static base path. - `pattern` (string): The remaining glob pattern. ### Example Usage ```go base, pattern := doublestar.SplitPattern("../../configs/**/*.yaml") fsys := os.DirFS(base) matches, err := doublestar.Glob(fsys, pattern) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d YAML files\n", len(matches)) ``` ``` -------------------------------- ### PathMatch string against pattern Source: https://github.com/bmatcuk/doublestar/blob/master/README.md PathMatch is similar to Match but automatically uses the system's path separator for splitting. It's intended as a replacement for filepath.Match and assumes consistent path separators in both pattern and name. ```go func PathMatch(pattern, name string) (bool, error) ``` -------------------------------- ### ValidatePattern for User Input Source: https://context7.com/bmatcuk/doublestar/llms.txt Checks if a glob pattern is valid before execution, useful for validating user input early. Patterns are automatically validated in `Match` and `Glob`, but pre-validation is efficient. ```go package main import ( "fmt" "github.com/bmatcuk/doublestar/v4" ) func main() { patterns := []string{ "**/*.go", // valid "src/{cmd,pkg}/**", // valid "[a-z]*.txt", // valid "[invalid", // invalid: unclosed character class "path/to/\", // invalid: trailing escape "{unclosed", // invalid: unclosed alternative } for _, p := range patterns { valid := doublestar.ValidatePattern(p) fmt.Printf("ValidatePattern(%q) = %v\n", p, valid) } // Output: // ValidatePattern("**/*.go") = true // ValidatePattern("src/{cmd,pkg}/**") = true // ValidatePattern("[a-z]*.txt") = true // ValidatePattern("[invalid") = false // ValidatePattern("path/to/\") = false // ValidatePattern("{unclosed") = false // Usage: validate user-supplied pattern before storing/using it userPattern := getUserInput() if !doublestar.ValidatePattern(userPattern) { fmt.Println("invalid glob pattern provided by user") return } // safe to use MatchUnvalidated for performance in hot loops _ = doublestar.MatchUnvalidated(userPattern, "some/file.txt") } func getUserInput() string { return "**/*.{go,mod}" } ``` -------------------------------- ### Match Path with Glob Patterns Source: https://context7.com/bmatcuk/doublestar/llms.txt Use doublestar.Match to test if a path string matches a glob pattern using '/' as the separator. It requires the pattern to match the entire name. ```go package main import ( "fmt" "github.com/bmatcuk/doublestar/v4" ) func main() { tests := []struct{ pattern, name string } { {"**/*.go", "src/utils/helper.go"}, // true - recursive match {"src/**/*.go", "src/utils/helper.go"}, // true {"src/**/*.go", "src/helper.go"}, // true - ** matches zero dirs {"*.go", "src/helper.go"}, // false - * doesn't cross / {"{cmd,pkg}/**", "cmd/server/main.go"}, // true - alternatives {"docs/v[0-9]/**", "docs/v2/api.md"}, // true - character class {"**/*_test.go", "pkg/auth/auth_test.go"}, // true } for _, tt := range tests { matched, err := doublestar.Match(tt.pattern, tt.name) if err != nil { fmt.Printf("ERROR pattern=%q: %v\n", tt.pattern, err) continue } fmt.Printf("Match(%q, %q) = %v\n", tt.pattern, tt.name, matched) } } ``` -------------------------------- ### Match Source: https://context7.com/bmatcuk/doublestar/llms.txt Reports whether a path string matches a glob pattern using '/' as the path separator. It's a replacement for path.Match and requires the pattern to match the entire name. ```APIDOC ## Match ### Description Reports whether a path string matches a glob pattern using `/` as the path separator. It requires the pattern to match the entire name (not just a substring) and returns `ErrBadPattern` if the pattern is malformed. Use `PathMatch` instead when working with OS-native path separators (e.g., `\` on Windows). ### Method `Match(pattern string, name string) (matched bool, err error)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go package main import ( "fmt" "github.com/bmatcuk/doublestar/v4" ) func main() { tests := []struct{ pattern, name string }{ {"**/*.go", "src/utils/helper.go"}, {"src/**/*.go", "src/utils/helper.go"}, {"src/**/*.go", "src/helper.go"}, {"*.go", "src/helper.go"}, {"{cmd,pkg}/**", "cmd/server/main.go"}, {"docs/v[0-9]/**", "docs/v2/api.md"}, {"**/*_test.go", "pkg/auth/auth_test.go"}, } for _, tt := range tests { matched, err := doublestar.Match(tt.pattern, tt.name) if err != nil { fmt.Printf("ERROR pattern=%q: %v\n", tt.pattern, err) continue } fmt.Printf("Match(%q, %q) = %v\n", tt.pattern, tt.name, matched) } } ``` ### Response #### Success Response (200) - **matched** (bool) - True if the path matches the pattern, false otherwise. - **err** (error) - An error if the pattern is malformed, nil otherwise. #### Response Example ``` Match("**/*.go", "src/utils/helper.go") = true Match("src/**/*.go", "src/utils/helper.go") = true Match("src/**/*.go", "src/helper.go") = true Match("*.go", "src/helper.go") = false Match("{cmd,pkg}/**", "cmd/server/main.go") = true Match("docs/v[0-9]/**", "docs/v2/api.md") = true Match("**/*_test.go", "pkg/auth/auth_test.go") = true ``` ``` -------------------------------- ### Glob Source: https://context7.com/bmatcuk/doublestar/llms.txt Returns all file names in an `fs.FS` that match the given pattern. It supports `**` and `{alts}` and is a drop-in replacement for `io/fs.Glob`. ```APIDOC ## Glob ### Description Returns all file names in an `fs.FS` that match the given pattern. It supports `**` and `{alts}` and is a drop-in replacement for `io/fs.Glob`. Patterns must use `/` as a separator. It silently ignores I/O errors by default; pass `WithFailOnIOErrors` to change this behavior. Patterns containing `/./`, `/../`, or starting with `/` return no results. ### Function Signature `doublestar.Glob(fsys fs.FS, pattern string, options ...doublestar.GlobOption) ([]string, error)` ### Parameters - **fsys** (fs.FS) - The file system to search within. - **pattern** (string) - The glob pattern to match. - **options** (...doublestar.GlobOption) - Optional configuration for globbing behavior (e.g., `WithFilesOnly`, `WithNoHidden`, `WithFailOnIOErrors`, `WithFailOnPatternNotExist`). ### Return Value - ([]string) - A slice of strings containing the paths that match the pattern. - (error) - An error if one occurs during the glob operation, depending on the options provided. ### Example ```go import ( "fmt" "os" "github.com/bmatcuk/doublestar/v4" ) func main() { // Example 1: basic glob on os.DirFS fsys := os.DirFS("/usr/local") matches, err := doublestar.Glob(fsys, "**/*.go") if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d .go files under /usr/local\n", len(matches)) // Example 2: alternatives pattern - match multiple extensions fsys2 := os.DirFS(".") matches2, err := doublestar.Glob(fsys2, "**/*.{go,mod,sum}", doublestar.WithFilesOnly(), // skip directories doublestar.WithNoHidden(), // skip dotfiles/dot-directories ) if err != nil { fmt.Println("error:", err) return } for _, m := range matches2 { fmt.Println(m) } // Example 3: only return directories matching pattern matches3, _ := doublestar.Glob(os.DirFS("."), "**/testdata/") for _, m := range matches3 { fmt.Println("dir:", m) } // Example 4: fail on I/O errors and on non-existent base path _, err = doublestar.Glob(os.DirFS("."), "nonexistent/**", doublestar.WithFailOnIOErrors(), doublestar.WithFailOnPatternNotExist(), ) if err != nil { fmt.Println("expected error:", err) // doublestar.ErrPatternNotExist } } ``` ``` -------------------------------- ### FilepathGlob Source: https://context7.com/bmatcuk/doublestar/llms.txt Performs a recursive glob on the filesystem, returning paths that use the OS-specific path separator. It acts as a replacement for `path/filepath.Glob` and handles `io/fs` complexity internally. ```APIDOC ## FilepathGlob Performs a recursive glob on the filesystem, returning paths that use the OS-specific path separator. It acts as a replacement for `path/filepath.Glob` and handles `io/fs` complexity internally. ### Function Signature `FilepathGlob(pattern string, options ...Option) ([]string, error)` ### Parameters - `pattern` (string): The glob pattern to match. - `options` ([]Option): Optional configuration for the glob operation (e.g., `WithCaseInsensitive`, `WithFilesOnly`, `WithNoFollow`). ### Returns - `[]string`: A slice of strings representing the file paths that match the pattern. - `error`: An error if the glob operation fails. ### Example Usage ```go matches, err := doublestar.FilepathGlob("../../myproject/**/*.go") if err != nil { fmt.Println("error:", err) return } for _, m := range matches { fmt.Println(m) } matches2, err := doublestar.FilepathGlob("/data/reports/**/*.CSV", doublestar.WithCaseInsensitive(), doublestar.WithFilesOnly()) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Found %d CSV files\n", len(matches2)) ``` ``` -------------------------------- ### SplitPattern Source: https://github.com/bmatcuk/doublestar/blob/master/README.md A utility function that splits a pattern into a base path and a pattern string. It returns everything up to the last slash before any unescaped meta characters as the base, and the rest as the pattern. This is useful for initializing `os.DirFS()` before calling `Glob()`. ```APIDOC ## SplitPattern ### Description SplitPattern is a utility function. Given a pattern, SplitPattern will return two strings: the first string is everything up to the last slash (`/`) that appears _before_ any unescaped "meta" characters (ie, `*?[{`). The second string is everything after that slash. For example, given the pattern: ``` ../../path/to/meta*/** ^----------- split here ``` SplitPattern returns "../../path/to" and "meta*/**". This is useful for initializing os.DirFS() to call Glob() because Glob() will silently fail if your pattern includes `/./` or `/../`. For example: ```go base, pattern := SplitPattern("../../path/to/meta*/**") fsys := os.DirFS(base) matches, err := Glob(fsys, pattern) ``` If SplitPattern cannot find somewhere to split the pattern (for example, `meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in this example). Note that SplitPattern will also unescape any meta characters in the returned base string, so that it can be passed straight to os.DirFS(). Of course, it is your responsibility to decide if the returned base path is "safe" in the context of your application. Perhaps you could use Match() to validate against a list of approved base directories? ### Signature ```go func SplitPattern(p string) (base, pattern string) ``` ```