fzf: fuzzy finder для shell
Терминал по умолчанию — это ввод команд по памяти и Tab-completion. Когда нужен файл в большом дереве, или один из 200 коммитов, или процесс из 50 в ps aux — Tab не помогает, ты помнишь только кусок названия. Стандартный workflow: набираешь find / -name 'partial*' 2>/dev/null или ps aux | grep partial — медленно, требует точного pattern.
fzf — fuzzy finder. Получает на stdin список строк, показывает интерактивный UI с fuzzy search, возвращает выбранную строку в stdout. На любую задачу «найди в списке» fzf даёт O(1) UX вместо O(N) ручного парсинга.
Установив fzf, ты получаешь: Ctrl-R с fuzzy search через всю historу, cd **<TAB> для fuzzy-перехода в директорию, fuzzy git branch checkout, fuzzy kill процесса, fuzzy file open в editor. Junior DE экономит десятки часов в год.
Установка
# Debian/Ubuntu:
sudo apt install fzf
# macOS:
brew install fzf
$(brew --prefix)/opt/fzf/install # установить shell integrations
# Из исходников (рекомендуется для последней версии):
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
При установке через install скрипт автоматически добавит в ~/.bashrc / ~/.zshrc source-команды для интеграций:
# ~/.bashrc или ~/.zshrc:
[ -f ~/.fzf.bash ] && source ~/.fzf.bash
Перезайди в терминал или сделай source ~/.bashrc.
Базовое использование
fzf — это pipe | fzf. Получает список строк, показывает интерфейс, возвращает выбранную.
# Найти файл в текущем дереве:
find . -type f | fzf
# (UI открывается, набираешь куски имени, Enter выбирает, путь печатается в stdout)
# Открыть выбранный файл в редакторе:
nano "$(find . -type f | fzf)"
# cd в выбранную директорию:
cd "$(find . -type d | fzf)"
Fuzzy matching syntax
fzf поддерживает паттерны:
foo bar— содержит и foo, и bar (fuzzy)^foo— начинается с foofoo$— заканчивается foo'foo— exact match foo (single quote prefix)!foo— не содержит foo^foo$— точное равенство foo
Это покрывает 99% потребностей поиска.
Key bindings из коробки
После установки с интеграциями в bash/zsh — три hotkeys:
Ctrl-R: fuzzy history search
Самая жизнеменяющая фича. По умолчанию Ctrl-R в bash — это поиск по истории точной подстроки. С fzf — fuzzy + полноэкранный preview:
> docker run <- ты набрал
docker run -it --rm ubuntu:24.04 bash <- match 1
docker run -d -p 80:80 nginx <- match 2
docker run --network=host alpine <- match 3
...
Стрелки выбирают, Enter подставляет в командную строку. Если случайно нажал — Escape отменяет.
Это самая важная фича fzf. Установи только ради Ctrl-R, остальное — приятный бонус. После недели использования вернуться к стандартному поиску по истории невозможно.
Ctrl-T: fuzzy find file
Подставляет выбранный файл в текущую командную строку. Полезно когда не помнишь точный путь:
$ cat <Ctrl-T>
# fzf открывается, ищет файл, выбираешь
$ cat ./src/config/database.yml
Alt-C: fuzzy cd to subdirectory
Жмёшь Alt-C — fzf показывает все subdirectories под текущей, выбираешь, cd выполняется.
Tab-completion: ** trigger
$ vim **<TAB>
# fzf открывается с файлами
$ cd **<TAB>
# fzf открывается с директориями
$ kill **<TAB>
# fzf открывается с процессами
$ ssh **<TAB>
# fzf открывается с known hosts
**<TAB> — universal trigger. fzf понимает контекст команды и выбирает подходящий source.
DE use case 1: git branch switching
# Функция в ~/.bashrc:
gcb() {
local branch
branch=$(git branch --all | grep -v HEAD | sed 's/^[* ]*//' | fzf) || return
git checkout "${branch#remotes/origin/}"
}
Теперь gcb показывает fuzzy-список веток, ты выбираешь — git checkout. Особенно полезно в репозиториях с 50+ ветками.
Аналог через git extras или forgit — там уже встроено.
DE use case 2: kill процесса fuzzy
fkill() {
local pid
pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')
[ -n "$pid" ] && echo "$pid" | xargs kill -"${1:-TERM}"
}
fkill — fuzzy-выбор процесса, kill с TERM (или другим сигналом).
-m (multi-select) — Tab помечает несколько строк, можно убить пачкой.
DE use case 3: Airflow DAG search
В Airflow часто 100+ DAGs. ls /opt/airflow/dags + grep — медленно.
# Функция:
adag() {
local dag
dag=$(find /opt/airflow/dags -name '*.py' | fzf --preview 'head -100 {}')
[ -n "$dag" ] && nano "$dag"
}
--preview — окно справа показывает первые 100 строк выбранного файла. Можно листать стрелками без открытия.
—preview: powerful UI
# С bat (syntax highlighting):
find . -name '*.py' | fzf --preview 'bat --color=always {}'
# С jq для JSON:
ls *.json | fzf --preview 'jq . {}'
# Git log preview:
git log --oneline | fzf --preview 'git show {1}'
{} подставляется выбранной строкой. {1}, {2} — отдельные слова из неё (split по whitespace).
DE use case 4: kubernetes context/pod switching
kubectl — полное управление кластером из командной строки# Switch kube context:
kctx() {
local ctx
ctx=$(kubectl config get-contexts -o name | fzf) && kubectl config use-context "$ctx"
}
# Open shell in pod:
ksh() {
local pod
pod=$(kubectl get pods -o name | fzf) && kubectl exec -it "$pod" -- bash
}
# Tail logs:
klog() {
local pod
pod=$(kubectl get pods -o name | fzf) && kubectl logs -f "$pod"
}
Это стандартный DE-toolbelt. Каждый день экономит минуты на навигации.
DE use case 5: log search
# Открыть лог-файл за вчера:
fls() {
find /var/log -name '*.log' -mtime -2 2>/dev/null \
| fzf --preview 'tail -50 {}' \
| xargs -I{} less {}
}
fls — fuzzy log search, preview tail, less для просмотра.
Или с ripgrep (см. урок 03) для поиска по содержимому:
# Найти лог-файлы с ERROR:
rg -l ERROR /var/log | fzf --preview 'rg --color=always ERROR {}' | xargs less
Customization через FZF_DEFAULT_OPTS
# ~/.bashrc:
export FZF_DEFAULT_OPTS="
--height=50%
--layout=reverse
--border
--info=inline
--preview-window=right:60%:wrap
--bind='ctrl-d:preview-half-page-down,ctrl-u:preview-half-page-up'
"
--height=50%— окно занимает половину экрана, не fullscreen.--layout=reverse— prompt сверху, results снизу (привычнее для tail).--border— рамка вокруг fzf.--preview-window=right:60%— preview справа, 60% ширины.
Также можно настроить command для дефолтного поиска файлов:
# Использовать fd вместо find (быстрее, см. урок 03):
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_ALT_C_COMMAND='fd --type d --hidden --exclude .git'
С fd дефолтный поиск становится в 5-10 раз быстрее (особенно на больших монорепозиториях).
fzf в скриптах: —select-1, —exit-0
Для использования в production-скриптах:
# Если матчей 0 — exit 0 без UI:
ls *.csv | fzf --exit-0
# Если матч 1 — автоселект без UI:
ls *.csv | fzf --select-1
# Часто вместе: подходит для CI/non-interactive:
ls *.csv | fzf --select-1 --exit-0 --filter "alice"
--filter — non-interactive фильтрация (используется как лучший grep с fuzzy логикой).
fzf + multi-select для batch operations
# Удалить несколько файлов (Tab помечает):
find /tmp -name '*.tmp' | fzf -m | xargs rm
# Или add to git multiple files:
git status --short | fzf -m | awk '{print $2}' | xargs git add
-m или --multi — multi-select. Tab помечает, Shift-Tab снимает, Enter завершает выбор.
Альтернативы и комплементы
Подводные камни
1. Multi-line input
fzf обрабатывает построчно. Если строка содержит newline (CSV с многострочными значениями) — fzf увидит несколько кандидатов. Решение: pre-process input.
2. Slow on huge inputs
Default find может быть медленным на больших file trees (миллионы файлов). fd или ripgrep с правильными flags решают. Также --no-sort ускоряет, если не нужна сортировка.
3. Кавычки и spaces в filenames
fzf сам корректно возвращает строку с пробелами. Но обязательно quote её в bash:
# OK:
file=$(find . | fzf)
cat "$file"
# СЛОМАЕТСЯ:
cat $(find . | fzf) # word splitting на пробелы
4. Скрипт vs interactive
Helpers (gcb, fkill) должны жить в ~/.bashrc/~/.zshrc или подключаемых файлах. В production-скриптах fzf редко нужен (там автоматизация без UI). Не путай interactive productivity и production.
Cross-links
- Урок 01 (jq) — fzf + jq:
cat data.json | jq -c '.[]' | fzf --preview 'echo {} | jq .'. - Урок 03 (ripgrep/fd) — основные источники для fzf.
- Урок 04 (tmux) — fzf для switching windows/panes.
- Модуль 02 (terminal config) — где конфигурировать fzf в shell.
Попробуй сам
-
Установи fzf и shell integrations. Перезайди в терминал.
-
Жми Ctrl-R, начинай печатать кусок недавней команды. Сравни с дефолтным Ctrl-R.
-
Попробуй
vim **<TAB>— fuzzy file picker. -
Создай функцию в
~/.bashrc:
fcd() {
local dir
dir=$(find . -type d 2>/dev/null | fzf --preview 'ls -la {}') && cd "$dir"
}
После source — fcd для fuzzy navigation.
- Git branch checkout:
gcb() {
local branch
branch=$(git branch --all | grep -v HEAD | sed 's/^[* ]*//' | fzf) || return
git checkout "${branch#remotes/origin/}"
}
- fzf + ripgrep — найти файлы с ERROR:
# Установи ripgrep сначала: apt install ripgrep / brew install ripgrep
rg -l ERROR /var/log/ 2>/dev/null | fzf --preview 'rg ERROR {}'