Skip to content
noahdunnaganPublic

About

Whole-disk file search for macOS: fuzzy names, typo tolerance, indexed content grep. ~1 ms over 8M files.

Resources

Stars

1.1k stars

Watchers

5 watching

Forks

Latest commit

 

History

34 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FSearch

Whole-disk file search for macOS. Finds any file by name in about a millisecond, forgives typos, and searches inside files with an index. Use it as a CLI (with a small daemon) or as a Rust crate.

cargo build --release && ./target/release/fsearch install   # -> ~/.local/bin/fsearch
fsearch fsearch main              # find files by name
fsearch 'ext:rs grep:apply_dir'   # search inside files

Speed

M4 Max, 7.7M files and folders on disk.

find a file by name, whole disk p50 1.3 ms
search inside files p50 9 ms
a new, renamed or deleted file shows up ~0.1 s
first crawl of the disk ~20 s, once
daemon memory 30-135 MB

vs fff

Chromium (509k files), same Mac, same queries. Video: demo/fsearch-vs-fff.mp4, method: demo/vs_fff.py.

fsearch fff
find a file by name 1.1 ms 13.8 ms
search inside files 5.6 ms 53 ms
typo still finds the file first 98% 88%
ready after launch 50 ms 2.5 s
memory 50 MB (whole disk) 358 MB (that folder)

On the smaller Linux kernel (96k files), name search is a tie and fsearch wins the rest. fff searches the contents of about 9% more files, because fsearch skips some file types and build/ and vendor/ folders.

Queries

fsearch 'readme in:~/Developer'          # inside a folder
fsearch 'type:image size:>5mb mtime:<7d'
fsearch 'ext:rs regex:fn\s+\w+_dir'      # regex inside files
fsearch 'sym:apply_dir'                  # where it's defined

Words are fuzzy, and 5+ letter words forgive one typo (mian.rs finds main.rs). Also 'exact, ^prefix, suffix$ and !exclude. Filters: ext: type: kind: in: size: mtime: re: path: grep: regex: sym: limit:. Content search is smart-case.

Full Disk Access

Started from a terminal with Full Disk Access, it indexes everything. As a login item (fsearch install --login), give ~/.local/bin/fsearch its own grant in System Settings > Privacy & Security, again after each rebuild. Without access it skips the protected folders instead of popping a prompt.

API

JSON lines over ~/Library/Application Support/FSearch/fsearch.sock, or fsearch stdio:

{"q": "fsearch main", "limit": 20}
{"op": "grep", "pattern": "apply_dir", "in": "~/Developer"}

Or link the crate:

let engine = fsearch::Engine::start(fsearch::Options { dir: fsearch::default_dir(&home), home: home.clone(), skip: None })?;
let hits = engine.search(&fsearch::Query::parse("fsearch main", &home)?)?;

An app and the CLI share one index: the first process owns it and the others follow along.

How it works

  • Crawls the disk once with getattrlistbulk, then stays current from FSEvents. A restart replays only what changed.
  • Names live in one mmap'd file, laid out folder by folder so in: is a range. Each distinct name is scored once.
  • Content search uses a trigram index of your text files. Matches are read fresh from disk, so they're never stale.

About

Whole-disk file search for macOS: fuzzy names, typo tolerance, indexed content grep. ~1 ms over 8M files.

Resources

Stars

1.1k stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages