Open-source project
bmatcuk/doublestar avatar
bmatcuk/doublestar

doublestar: recursive glob matching for Go

Implements support for double star (**) matches in golang's path.Match and filepath.Glob.

720 stars80 forksGoMIT

At a glance

What is it?
doublestar is a Go implementation of path pattern matching and globbing that supports the globstar `**` pattern the standard library lacks, with a v4 rewrite built on io/fs and a set of glob options for case sensitivity, IO errors, files-only results and symlink following. It suits Go developers whose file discovery needs to reach across directory trees, and its API is a deliberate near-drop-in for path.Match and filepath.Match.
Who is it for?
doublestar fits a Go project that needs recursive file discovery and has outgrown the standard library's single-star globbing, and it does not fit a codebase that only needs path.Match semantics or that cannot move to Go 1.16 or later.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap: Go's standard library stops at one star

doublestar is a Go implementation of path pattern matching and globbing with support for doublestar, also known as globstar, patterns.

The problem it solves is small and specific. The standard library has path.Match and filepath.Match, and both treat a single asterisk as matching within one path component. A pattern like filepath.Glob("dir/*/*.go") reaches exactly two levels down and no further. Anything that has to walk an arbitrary tree needs recursive matching, and doublestar provides it.

The readme is careful about the syntax, and this is where most people get it wrong. The doublestar must appear as a path component by itself. A pattern such as `/path**` is invalid and will be treated the same as /path*, but `/path*/**` achieves the desired result. And the trailing slash matters: `/path/**` matches all directories and files under the path directory, while `/path/**/` matches only directories.

Bash's globstar is the stated inspiration, and it works similarly, so anyone who has written `**` in a shell script already knows the shape of the pattern.

The audience is Go developers writing file discovery, build tooling, or configuration loaders, and the licensing is MIT. The module path is github.com/bmatcuk/doublestar/v4, and the go.mod declares go 1.16, which is the floor that comes from the io/fs migration.

The v4 rewrite and the io/fs dependency

The readme states that v4 is a complete rewrite with a focus on performance, and that doublestar has been updated to use the new io/fs package for filesystem access, and as a result it is only supported by Go v1.16 and above.

That io/fs move is the substantive change rather than a cosmetic one. io/fs gives Go a filesystem abstraction, fs.FS, that lets a program operate against something that is not the real disk, and an fs.WalkDir-style traversal with the DirEntry type that is cheaper than a stat call per entry because the traversal already knows whether an entry is a directory. A glob library that walks a tree is exactly the workload that benefits.

The cost is the version floor. Go 1.16 is old enough that most projects have moved past it, but if you are maintaining something on an older toolchain, the v4 module is not available to you and you would have to look for an older release line. The repository has an UPGRADING.md file, and the readme links it directly under the About section, which is a reasonable signal that a v3 to v4 migration exists and is not trivial.

The package layout is flat and readable: doublestar.go, glob.go, globoptions.go, globwalk.go, match.go, utils.go, validate.go, plus platform-specific utils_unix.go and utils_windows.go files, and a test file beside each. The separate glob options file is a hint about the API shape, since the options are their own type rather than a struct you build.

Match versus PathMatch, and the separator question

Four matching functions sit at the core, and picking between them is the first decision a user has to make.

Match is described as a drop-in replacement for path.Match, and it splits name and pattern on forward slash characters, which may be relative or absolute. It requires the pattern to match all of name, not just a substring, and the only error it can return is ErrBadPattern when the pattern is malformed. Because it always uses forward slash as the separator, on a system with a different separator such as Windows, what you want is PathMatch, or you can run filepath.ToSlash on both the pattern and the name and use Match instead.

PathMatch is the mirror image: it is a drop-in replacement for filepath.Match, it automatically uses your system's path separator to split name and pattern, and on systems where the separator is a backslash, escaping is disabled. That last detail matters, because a Windows path with a backslash is not the same thing as a pattern that escapes a character.

Then there are the unvalidated variants. MatchUnvalidated and PathMatchUnvalidated skip the pattern validity check and return a plain bool, which the readme says can give a small performance improvement when you do not care about whether the pattern is valid, perhaps because you already ran ValidatePattern. The important caveat is that the improvement is only realised in one case, when pattern matching reaches the end of name before reaching the end of pattern, such as Match("a/b/c", "a").

ErrBadPattern is returned by various functions to report that the pattern is malformed, and the readme notes that the value currently equals path.ErrBadPattern but that, for portability, this equivalence should probably not be relied upon. So compare against doublestar.ErrBadPattern, not path.ErrBadPattern.

Glob options: case sensitivity, IO errors, files only, symlinks

The glob functions accept options as trailing arguments, any number of them and in any order, and the four documented ones are worth understanding individually because each changes what a glob means.

WithCaseInsensitive makes doublestar treat all alphabetic characters as case insensitive, so a in the pattern matches a or A. The readme says this is useful for platforms like Windows where paths are case insensitive by default, and it also says the comparison is naïve, simply using unicode.ToLower() to compare. That is a real caveat: a naïve lowercasing comparison is not the same as a proper case fold, and it does not handle the case folding rules that Unicode specifies for scripts like Turkish.

WithFailOnIOErrors makes doublestar abort and return IO errors when it encounters them. Without it, IO problems are swallowed. The companion option WithFailOnPatternNotExist makes doublestar abort and return doublestar.ErrPatternNotExist if the pattern references a path that does not exist before any meta characters such as nonexistent/path/*, and the readme explains the subtlety: alts, that is {...} groups, are expanded before this check, so a pattern such as {a,b}/* may fail if either a or b does not exist, while */{a,b} will never fail because the star may match nothing.

WithFilesOnly restricts the results to things that are not a directory and not a symlink to a directory. WithNoFollow stops doublestar following symlinks while traversing the filesystem, with a caveat that the readme states plainly: because io/fs has very poor support for querying the filesystem about symlinks, if part of the pattern before any meta characters contains a reference to a symlink, it will be followed anyway. A pattern like path/to/symlink/* is the example given.

That caveat is the kind of thing that decides whether you can use a glob result for anything security-sensitive.

Installing and using it in a Go project

The package is a normal Go module, installed with go get:

bash
go get github.com/bmatcuk/doublestar/v4

And imported like any other:

go
import "github.com/bmatcuk/doublestar/v4"

The module path carries the major version, which is the /v4 suffix, so a project that upgrades from v3 changes its import path, not just its dependency version. That is the standard Go convention and it is why the repository carries UPGRADING.md, but it does mean the migration touches every file that imports the package.

The repository includes an examples directory with a find.go program and its own go.mod, so the package is a separate module from the example. The structure of the tree is otherwise flat, with the top-level Go files at the root of the module rather than under a directory, which is the older but still common layout for a package of this size.

One thing the readme does not include is a worked example of a glob call with the options attached, so the practical entry point is the function signatures themselves, which are given as complete Go declarations:

go
func Match(pattern, name string) (bool, error)

and

go
func PathMatch(pattern, name string) (bool, error)

with the GlobOption values documented separately as standalone option functions.

State of the project: version 4.10, three releases in a month

The release history is recent and the fix cadence is fast. The most recent tags are v4.10.3, whose release note says it handles character ranges when using WithCaseInsensitive, v4.10.2, which brings better support for Windows junctions, and v4.10.1, which fixed an issue with alternatives and character classes.

Those three titles are a good summary of where a glob library spends its time, and they are also a warning. Character ranges and alternatives are exactly the corners of the pattern grammar where a hand-written matcher is most likely to disagree with a user's expectation, and the fact that v4.10.1 and v4.10.3 both fix pattern-matching edge cases means that if you adopt a version older than 4.10.3, you are running a matcher with known bugs in exactly the syntax people use for character classes.

The repository is not archived, and the last push was on 2026-10-11, the same day as the v4.10.3 tag. The licence is MIT, and the project publishes a codecov coverage badge and a build status badge, so the test suite is tracked as a first-class concern.

The pattern-matching rules themselves are documented in the readme under a patterns link

Editorial conclusion

doublestar fits a Go project that needs recursive file discovery and has outgrown the standard library's single-star globbing, and it does not fit a codebase that only needs path.Match semantics or that cannot move to Go 1.16 or later. Before adopting it, decide between Match and PathMatch for your platform, since Match always splits on forward slash, and check whether the default behaviour of returning a nil error for a path that does not exist is what your caller expects, because WithFailOnPatternNotExist is opt-in.

Frequently asked questions

What problem does the doublestar Go library solve?

It implements path pattern matching and globbing in Go with support for globstar, the `**` pattern, so a pattern can match files and directories recursively. The standard library's path.Match and filepath.Match only match within a single path component, which is the limitation doublestar exists to remove.

How do I install and import doublestar?

Run go get github.com/bmatcuk/doublestar/v4, then import it with import "github.com/bmatcuk/doublestar/v4". The module path includes the major version suffix, so upgrading from v3 changes the import path in every file that uses the package.

When should I use Match instead of PathMatch?

Match is a drop-in replacement for path.Match and always splits on forward slash, so it suits relative or absolute slash-separated paths. PathMatch is a drop-in replacement for filepath.Match and uses your system path separator, which matters on Windows where escaping is disabled.

What do the doublestar glob options do?

WithCaseInsensitive makes alphabetic matching case insensitive using a naive unicode.ToLower comparison. WithFailOnIOErrors aborts on IO errors, and WithFailOnPatternNotExist aborts with doublestar.ErrPatternNotExist when a path before any meta characters does not exist. WithFilesOnly excludes directories and symlinks to directories, and WithNoFollow avoids following symlinks during traversal, with the caveat that a symlink in the fixed part of the pattern is still followed.

What Go version does doublestar v4 require?

Go 1.16 or later, because v4 was updated to use the io/fs package for filesystem access. The go.mod in the repository declares go 1.16, and the readme links an UPGRADING.md for people moving from the previous major version.

Official sources

  1. bmatcuk/doublestar on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bmatcuk-doublestar.svg)](https://hysenlabs.com/projects/bmatcuk-doublestar)

Was this useful?