Skip to main content

Search Operators and Scoring

Understanding how fzf scores matches helps you write better queries and design more effective fzf-based tools.

The Scoring Algorithm

fzf uses the fzf (v2) scoring algorithm. It assigns scores based on:

Higher score = better match
FactorEffect on score
Consecutive matched chars+++ Higher score
Match at word boundary (-, /, ., _, space)++ Higher score
Match at start of string++ Higher score
Match at start of basename++ Higher score
Far apart matched chars- Lower score
Matched chars only in middle- Lower score

Example Scoring

Query: "mvc"

Candidate: mvc-framework ← score: 90 (all chars at start, consecutive)
Candidate: my-view-controller ← score: 75 (word boundaries but not consecutive)
Candidate: some-movie-creator ← score: 40 (chars spread out)

Controlling Sort

# Default: sorted by match score (best first)
ls | fzf

# Disable sort: maintain input order
ls | fzf --no-sort

# Toggle sort interactively
ls | fzf --bind "ctrl-s:toggle-sort"

The --tiebreak Flag

When multiple items have equal scores, --tiebreak determines which comes first:

fzf --tiebreak=length      # shorter strings first
fzf --tiebreak=begin # earlier match position first
fzf --tiebreak=end # later match position first
fzf --tiebreak=index # original input order
fzf --tiebreak=chunk # chunk-based (default for large items)

# Combine multiple tiebreakers
fzf --tiebreak=length,begin,index

The --algo Flag

fzf --algo=v1     # simpler algorithm (faster, less accurate)
fzf --algo=v2 # default: full fuzzy algorithm (best accuracy)

Use v1 only for extremely large lists (millions of items) where speed matters more than precision.

Tabular Data — --nth and --delimiter

When input is tabular (CSV, TSV, space-separated), restrict scoring to specific columns:

# Score only against column 2 of space-separated ps output
ps aux | fzf --nth=2

# Score against columns 1 and 3 of colon-separated /etc/passwd
cat /etc/passwd | fzf --delimiter=: --nth=1,3

# Score against everything after the first tab
cat data.tsv | fzf --delimiter=$'\t' --nth=2..

# Score only basename of path (ignore directory part)
find . -name "*.conf" | fzf --nth=-1 --delimiter=/
# -1 means last field (basename)

--scheme — Optimized Match Scoring per Use Case

fzf --scheme=default     # balanced: file/non-file mix
fzf --scheme=path # optimized for file paths
fzf --scheme=history # optimized for shell history (recent = higher)
fzf --scheme=history --tiebreak=index # shell history: recent items first

Complete Token Reference

TERM     Fuzzy match
'TERM Exact match
^TERM Starts with (prefix exact)
TERM$ Ends with (suffix exact)
^TERM$ Full line equals TERM exactly
!TERM NOT fuzzy match
!'TERM NOT exact match
!^TERM Does NOT start with
!TERM$ Does NOT end with

Multiple space-separated terms = AND
TERM1 | TERM2 = OR (must use spaces around |)

Realistic Multi-Token Queries

# nginx error logs from this year, excluding health checks
cat /var/log/nginx/access.log | fzf --query "'2024 '500 | '502 | '503 !'health"

# Find Python files, not in venv, not __pycache__
find . | fzf --query ".py$ !venv !__pycache__ !.pyc$"

# Docker: running containers with web in name, not paused
docker ps -a | fzf --query "web !Exited !Paused"

# Kubernetes pods in production, not running (find errors)
kubectl get pods -A | fzf --query "production !Running !Completed"

What's Next