Fast directory search and navigation for PowerShell, powered by a native Search-Directory cmdlet written in C#.
Built for very large source trees (Windows Source Code, Chromium, monorepos) where Get-ChildItem -Recurse is too slow. Search a quarter-million-file tree and jump to the match in a single command.
Search-Directory— a native cmdlet that streams matches as it walks the tree, with pluggable exclude-dir pruning, case-insensitive ordinal matching, wildcards, and multi-part path patterns.sd— thin PowerShell wrapper aroundSearch-Directorythat auto-detects the repo root (viaGet-RepoRoot) and excludes build-output dirs (obj,objd,objr,objc,bin,.git,node_modules).go— friendlycd-with-search: jumps to a named location from$go_locationsor falls back tosdand pushes the first match.- Cross-edition — targets
netstandard2.0, works on Windows PowerShell 5.1 and PowerShell 7 (Linux/macOS included). Path separators (\and/) are accepted interchangeably in patterns. - No binary in git —
SearchDir.dllis built from source bybuild.ps1; CI builds and publishes to the PowerShell Gallery.
From the PowerShell Gallery:
Install-Module PwrSearchFrom source (contributors):
git clone https://github.com/ocalvo/PwrSearch.git
cd PwrSearch
.\build.ps1 # dotnet build + copy SearchDir.dll next to the psm1
Import-Module .\PwrSearch.psd1Search-Directory `
-SearchDirectories C:\src `
-ExcludeDirectories 'obj','bin','.git' `
-Pattern 'MyProject' `
[-SubstringMatch $true] `
[-All]-SearchDirectories— one or more roots to search.-ExcludeDirectories— bare names (matched byName, case-insensitive) and/or rooted absolute paths (matched byFullName). Checks areHashSet<string>lookups, so excludes are O(1) per directory.-Pattern— match string. Backslash or forward slash splits it into ordered path parts:'src\tools'or'src/tools'matches anysrc/*/toolswhere both names match their respective directory. Wildcards (*) allowed inside each part.-SubstringMatch $true— match anywhere in the directory name (default is starts-with).-All— return every match. Without it, streams the first match and stops.
Results are emitted as DirectoryInfo objects, so you can pipe into any filesystem cmdlet:
Search-Directory -SearchDirectories C:\src -ExcludeDirectories obj,bin -Pattern 'tests' -All |
ForEach-Object { $_ | Get-ChildItem -Filter '*.cs' }Searches from the repo root (detected by Get-RepoRoot) if available, otherwise from the current location. Automatically excludes common build-output dirs.
sd foo.cpp # first match
sd foo.cpp -All # all matches
sd 'src\tools' # multi-part path pattern (\ or / both work)
sd src/tools # same as abovego home # ~
go src # C:\src (Windows only)
go bin # C:\bin (Windows only)
go scripts # $profile's directory
go <name> # any key in $go_locations, or an sd fallback searchExtend with your own:
$go_locations['myrepo'] = 'C:\repos\myrepo'Search-Directory walks the tree breadth-first but prioritizes already-matched path parts — once a pattern part matches, its child states move to a higher depth bucket, which drains first. Within a bucket, exact-name matches (strength 2) outrank substring matches (strength 1), so you typically hit the intended directory in the first few results even on huge trees.
Hot-path optimizations worth knowing about:
- Exclude-dir filtering is O(1) per directory (
HashSet<string>withOrdinalIgnoreCase), not O(n) over a predicate list. - No LINQ / no allocated enumerators in the child-iteration loop.
- Wildcard patterns compile to
RegexOptions.Compiled | CultureInvariant. - Non-wildcard matchers use
string.StartsWith/string.IndexOfwithStringComparison.Ordinal*— no culture tables, no per-call allocation. - Inaccessible directories are silently skipped (
UnauthorizedAccessException,DirectoryNotFoundException).
build.ps1 runs dotnet build on src/SearchDir.csproj and copies the resulting DLL next to PwrSearch.psm1:
.\build.ps1 # Release (default)
.\build.ps1 -Configuration DebugProject layout:
PwrSearch/
├── PwrSearch.psd1 # module manifest (ModuleVersion is x-release-please-version)
├── PwrSearch.psm1 # PowerShell glue: sd, _gosd, Switch-Location / go alias
├── SearchDir.dll # built by build.ps1 (gitignored)
├── build.ps1 # dotnet build + copy
├── src/
│ ├── SearchDir.csproj # SDK-style, netstandard2.0, PowerShellStandard.Library
│ ├── SearchDirectory.cs # the Search-Directory cmdlet
│ └── GetRepoRoot.cs # the Get-RepoRoot cmdlet
└── .github/workflows/
├── release-please.yml # opens/maintains a release PR from conventional commits
└── publish.yml # on release: setup-dotnet → build → verify manifest → Publish-Module
Versioning and publishing are driven by release-please:
- Commit with Conventional Commits (
feat:,fix:,feat!:…) onmain. release-please.ymlopens (or updates) a release PR that bumpsPwrSearch.psd1'sModuleVersionvia thex-release-please-versionmarker and regeneratesCHANGELOG.md.- Merge the release PR → GitHub creates a tagged release.
publish.ymlfires onrelease: published: sets up .NET, runsbuild.ps1, verifies the manifest version matches the tag, and runsPublish-Modulewith thePSGALLERY_API_KEYsecret.
MIT — see LICENSE.