sharkdp/fd
Search modes
Active contributors: sharkdp, tmccombs
Purpose
fd accepts a single search pattern (and optionally additional patterns via --and). This page covers the three interpretation modes — regex, glob, fixed-string — plus how case sensitivity is decided and how patterns can match either the file name or the full path.
The three pattern modes
| Mode | Flag | Meaning |
|---|---|---|
| Regex (default) | none | The pattern is treated as a Rust regex::bytes::Regex. Documented at https://docs.rs/regex/latest/regex/#syntax. |
| Glob | -g, --glob |
The pattern is parsed by globset::GlobBuilder and converted to a regex internally. literal_separator(true) means * does not cross / boundaries. |
| Fixed strings | -F, --fixed-strings, --literal |
The pattern is escaped via regex::escape, so every character is literal. Note: this still does substring matching (the regex is unanchored). |
The conversion happens in build_pattern_regex in src/main.rs:
fn build_pattern_regex(pattern: &str, opts: &Opts) -> Result<String> {
Ok(if opts.glob && !pattern.is_empty() {
let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;
glob.regex().to_owned()
} else if opts.fixed_strings {
regex::escape(pattern)
} else {
String::from(pattern)
})
}--glob and --fixed-strings are conflicts_with each other, declared on Opts::glob in src/cli.rs. --regex exists as an explicit override of --glob (e.g., to undo a --glob set by an alias).
Empty pattern
If the user runs fd with no positional pattern (or with an empty one), it returns every entry that survives the rest of the filters. Internally the empty pattern compiles to a regex that matches everything; build_pattern_regex short-circuits the glob branch when the pattern is empty so a plain fd works whether or not -g is in an alias.
Smart case
Default behaviour: case-insensitive unless the pattern contains a literal uppercase character anywhere in its parsed regex_syntax::Hir. Implemented by pattern_has_uppercase_char in src/regex_helper.rs. When --and patterns are present, the smart-case check applies to any pattern containing an uppercase character — the conjunction becomes case-sensitive as a whole.
| Flag | Effect |
|---|---|
-s, --case-sensitive |
Force case-sensitive. |
-i, --ignore-case |
Force case-insensitive. |
| (default) | Smart case — uppercase in pattern → case-sensitive. |
--ignore-case overrides --case-sensitive and vice versa via clap's overrides_with, so the rightmost flag wins on the command line.
Multiple patterns (--and)
--and is repeatable. All patterns must match for an entry to be included. Implementation:
Opts::exprs: Option<Vec<String>>collects them.pattern_regexpsinrun()chains the main pattern at the end ofexprsand runs each throughbuild_pattern_regex.spawn_senders(src/walk.rs) checkspatterns.iter().all(|pat| pat.is_match(...))against the search input.
A useful idiom: fd 'foo' --and '\.rs$' matches Rust files containing foo in the basename.
Filename vs. full path (-p/--full-path)
By default, patterns match against just the basename of each entry, computed inline in search_str_for_entry:
match entry_path.file_name() {
Some(filename) => Cow::Borrowed(filename),
None => unreachable!(...),
}With --full-path, the input becomes the absolute path. construct_config populates Config::full_path_base = Some(env::current_dir()?). search_str_for_entry then either returns the entry path as-is (if absolute) or joins it onto the base (after stripping a leading ./).
--full-path interacts with the path-pattern diagnostic: ensure_search_pattern_is_not_a_path returns immediately when opts.full_path is set, because slashes are now legitimate in the pattern.
The "you typed a path" diagnostic
Without --full-path, fd matches against file names. A pattern that contains / therefore can never match. To save the user a confused round-trip, ensure_search_pattern_is_not_a_path returns an error suggesting either fd . PATH or fd --full-path PATTERN. This is what makes:
fd /etc/passwdprint a friendly message rather than silent zero results.
The Windows-only branch additionally accepts the native \ separator, but only when the pattern resolves to an existing directory — otherwise valid regex patterns like \Acargo or \d+ would be falsely flagged.
This logic was tightened in PR #1975 (in the Unreleased section of CHANGELOG.md as of 2250bb0) to flag any forward-slash pattern unconditionally rather than only when it happens to name an existing directory.
Pattern compilation
After smart-case detection, build_regex (in src/main.rs) constructs each regex::bytes::Regex:
RegexBuilder::new(&pattern_regex)
.case_insensitive(!config.case_sensitive)
.dot_matches_new_line(true)
.build()dot_matches_new_line(true) matters because filenames could in principle contain newlines on Unix (rare, but possible). If compilation fails, the error message tells the user to consider --fixed-strings or --glob.
Anchoring
Patterns are unanchored by default. Users add ^…$ for full-string matches, e.g. fd '^x.*rc$' to find files starting with x and ending in rc. Glob mode is fully anchored via globset's default behaviour — *.txt only matches a complete basename ending in .txt because literal_separator(true) was passed.
Worked examples
| User wants | Command | What happens |
|---|---|---|
| Substring match anywhere in the basename | fd netfl |
Default regex; everything is treated literally because the pattern has no regex metacharacters. Smart case → case-insensitive (no uppercase). |
Glob *.tar.gz |
fd -g '*.tar.gz' |
globset converts to regex; literal_separator(true) keeps * from crossing dirs. |
| Path-aware regex | fd -p '.*/lesson-\d+/[a-z]+\.(jpg|png)' |
--full-path turns on absolute-path matching; the regex is unanchored, but the pattern itself constrains it. |
| Exact filename | fd -g libc.so |
Glob without wildcards behaves like a literal-match-on-basename. |
| Multiple constraints | fd 'foo' --and 'bar' --and '\.rs$' |
All three regexes must match the basename. |
Entry points for modification
- Add a new pattern syntax. Add a flag to
Opts, branch inbuild_pattern_regex, and document the interaction with-g/-F/smart-case. - Tweak smart-case detection. Edit
pattern_has_uppercase_charinsrc/regex_helper.rsand add tests inline. - Change basename-vs-fullpath defaults. This would be a major UX change; the relevant code is
search_str_for_entry(src/walk.rs) andConfig::full_path_base(src/config.rs).
Related pages
- CLI parsing — flag registration and override semantics.
- Walker — where the regex is applied per entry.
- Glossary — terms like "smart case" and "smart strip-cwd".
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.