Open-Source Wikis

/

fd

/

Features

/

Search modes

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_regexps in run() chains the main pattern at the end of exprs and runs each through build_pattern_regex.
  • spawn_senders (src/walk.rs) checks patterns.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/passwd

print 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 in build_pattern_regex, and document the interaction with -g/-F/smart-case.
  • Tweak smart-case detection. Edit pattern_has_uppercase_char in src/regex_helper.rs and 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) and Config::full_path_base (src/config.rs).
  • 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.

Search modes – fd wiki | Factory