1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-fzf

Клонировать/Скачать
ADVANCED.md 27 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 29.06.2025 17:06 a88d352

Продвинутые примеры использования fzf

  • Последнее обновление: 2025/02/02
  • Требуется fzf 0.59.0 или новее

Введение

fzf — это интерактивная программа-фильтр для Unix, предназначенная для использования с другими инструментами Unix. Она читает список элементов из стандартного ввода, позволяет вам выбрать подмножество этих элементов и выводит выбранные элементы на стандартный вывод. Можно представить её как интерактивную версию grep, и она уже полезна даже если вы не знаете ни одной из её опций.

# 1. ps:   Передача списка процессов в fzf
# 2. fzf:  Интерактивный выбор процесса с использованием алгоритма нечёткого поиска
# 3. awk:  Извлечение PID из выбранной строки
# 3. kill: Убийство процесса с этим PID
ps -ef | fzf | awk '{print $2}' | xargs kill -9

Хотя приведённый выше пример кратко описывает основную концепцию fzf, вы можете создавать гораздо более сложные интерактивные рабочие процессы, используя широкий спектр её возможностей.

  • Для просмотра полного списка опций и возможностей см. man fzf
  • Для просмотра последних добавлений см. CHANGELOG.md

Этот документ поможет вам ознакомиться с некоторыми примерами, которые познакомят вас с продвинутыми возможностями fzf.

Режимы отображения

--height

По умолчанию fzf открывается в полноэкранном режиме, но это не всегда удобно. Часто вам нужно видеть текущий контекст терминала при использовании fzf. Опция --height позволяет открывать fzf ниже курсора в неполноэкранном режиме, чтобы вы могли видеть предыдущие команды и их результаты над ним.

fzf --height=40%

image

Вы также можете экспериментировать с другими параметрами макета, такими как --layout=reverse, --info=inline, --border, --margin и т.д.

fzf --height=40% --layout=reverse
fzf --height=40% --layout=reverse --info=inline
fzf --height=40% --layout=reverse --info=inline --border
fzf --height=40% --layout=reverse --info=inline --border --margin=1
fzf --height=40% --layout=reverse --info=inline --border --margin=1 --padding=1

image

(См. справочную страницу для просмотра полного списка опций)

Но вы точно не захотите каждый раз повторять --height=40% --layout=reverse --info=inline --border --margin=1 --padding=1. Вы можете написать обёртывающий скрипт или алиас оболочки, но есть более простой вариант. Определите $FZF_DEFAULT_OPTS следующим образом:

export FZF_DEFAULT_OPTS="--height=40% --layout=reverse --info=inline --border --margin=1 --padding=1"

--tmux

(Требуется tmux 3.3 или новее)

Если вы используете tmux, вы можете открыть fzf в попапе tmux с помощью опции --tmux.

# Открыть fzf в попапе tmux по центру экрана с шириной и высотой 70%
fzf --tmux 70%

image

Опция --tmux игнорируется, если вы не используете tmux. Поэтому, если вы хотите избежать открытия fzf в полноэкранном режиме, попробуйте указать как --height, так и --tmux.

# --tmux указан позже, поэтому он имеет приоритет над --height при использовании tmux.
# Если вы не используете tmux, --tmux игнорируется, и используется --height.
fzf  --height 70% --tmux 70%

Вы также можете указать положение, ширину и высоту окна попапа в следующем формате:

  • [center|top|bottom|left|right][,SIZE[%]][,SIZE[%][,border-native]]
# 100% ширина и 60% высота
fzf --tmux 100%,60% --border horizontal

image

# Справа (50% ширина)
fzf --tmux right

image

# Слева (40% ширина и 70% высота)
fzf --tmux left,40%,70%

image

[!TIP] Вы также можете ознакомиться с моими плагинами для tmux, поддерживающими этот макет попапа.

Динамическое обновление списка

fzf может динамически обновлять список кандидатов с помощью произвольной программы с помощью привязок reload (документация по дизайну reload доступна здесь).

Обновление списка процессов при нажатии CTRL-R

Этот пример показывает, как можно настроить привязку для динамического обновления списка без перезапуска fzf.

(date; ps -ef) |
  fzf --bind='ctrl-r:reload(date; ps -ef)' \
      --header=$'Press CTRL-R to reload\n\n' --header-lines=2 \
      --preview='echo {}' --preview-window=down,3,wrap \
      --layout=reverse --height=80% | awk '{print $2}' | xargs kill -9

image

  • Начальная команда — (date; ps -ef). Она выводит текущую дату и время, а также список процессов.
  • С помощью опции --header можно показывать любое сообщение в фиксированном заголовке.
  • Чтобы запретить выбор первых двух строк (date и заголовок ps), мы используем опцию --header-lines=2.
  • --bind='ctrl-r:reload(date; ps -ef)' привязывает CTRL-R к действию reload, которое выполняет date; ps -ef, чтобы обновить список процессов при нажатии CTRL-R.
  • Мы используем простую привязку предварительного просмотра echo {}, чтобы видеть всю строку в окне предварительного просмотра ниже, даже если она слишком длинная.

Переключение между источниками данных

Вы не ограничены только одной привязкой к обновлению. Настройте несколько привязок, чтобы переключаться между источниками данных.

find * | fzf --prompt 'All> ' \
             --header 'CTRL-D: Directories / CTRL-F: Files' \
             --bind 'ctrl-d:change-prompt(Directories> )+reload(find * -type d)' \
             --bind 'ctrl-f:change-prompt(Files> )+reload(find * -type f)'

image

image

Переключение с помощью одного сочетания клавиш

Пример выше использует два разных сочетания клавиш для переключения между двумя режимами, но можно ли использовать одно сочетание клавиш?

Чтобы сделать так, чтобы сочетание клавиш вело себя по-разному при каждом нажатии, нам нужно:

  1. способ хранения текущего состояния. Например, "в каком режиме мы находимся?"
  2. способ динамического выполнения различных действий в зависимости от состояния.

Следующий пример показывает, как:

  1. хранить текущий режим в строке приглашения,
  2. использовать эту информацию ($FZF_PROMPT) для определения действий с помощью действия transform.
fd --type file |
  fzf --prompt 'Files> ' \
      --header 'CTRL-T: Switch between Files/Directories' \
      --bind 'ctrl-t:transform:[[ ! $FZF_PROMPT =~ Files ]] &&
              echo "change-prompt(Files> )+reload(fd --type file)" ||
              echo "change-prompt(Directories> )+reload(fd --type directory)"' \
      --preview '[[ $FZF_PROMPT =~ Files ]] && bat --color=always {} || tree -C {}'

Интеграция с Ripgrep

Использование fzf в качестве вторичного фильтра

  • Требуется bat
  • Требуется Ripgrep

fzf достаточно быстр для фильтрации списков, и редко приходится задумываться о его производительности. Но он не предназначен для поиска текста внутри больших файлов, и в этом случае следует использовать что-то вроде Ripgrep.

В следующем примере Ripgrep является основным фильтром, который ищет заданный текст в файлах, а fzf используется как вторичный нечёткий фильтр, добавляющий интерактивность к рабочему процессу. Мы используем bat для отображения соответствующей строки в окне предварительного просмотра.

Это скрипт bash, и он не будет работать как ожидается на других некомплиантных оболочках. Чтобы избежать проблем совместимости, сохраним этот фрагмент кода в виде скрипта под названием rfv.

#!/usr/bin/env bash

# 1. Поиск текста в файлах с помощью Ripgrep
# 2. Интерактивное уточнение списка с помощью fzf
# 3. Открытие файла в Vim
rg --color=always --line-number --no-heading --smart-case "${*:-}" |
  fzf --ansi \
      --color "hl:-1:underline,hl+:-1:underline:reverse" \
      --delimiter : \
      --preview 'bat --color=always {1} --highlight-line {2}' \
      --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
      --bind 'enter:become(vim {1} +{2})'

И запустите его с начальной строкой запроса.

# Сделайте скрипт исполняемым
chmod +x rfv

# Запустите его с начальным запросом "algo"
./rfv algo

Ripgrep выполнит начальный поиск и выведет все строки, содержащие algo. Затем мы уточним список на fzf.

image

Я знаю, что это много информации, давайте попробуем разобрать код.

  • Ripgrep выводит соответствующие строки в следующем формате
    man/man1/fzf.1:54:.BI "--algo=" TYPE
    man/man1/fzf.1:55:Fuzzy matching algorithm (default: v2)
    man/man1/fzf.1:58:.BR v2 "     Optimal scoring algorithm (quality)"
    src/pattern_test.go:7:  "github.com/junegunn/fzf/src/algo"
    Первый токен, разделённый символом :, — это путь к файлу, а второй токен — номер строки соответствующей строки. Они соответственно соответствуют {1} и {2} в команде предварительного просмотра.
    • --preview 'bat --color=always {1} --highlight-line {2}'
  • Поскольку мы запускаем rg с опцией --color=always, мы должны сказать fzf анализировать коды ANSI в входных данных, установив --ansi.
  • Мы настраиваем цвет различных текстовых элементов с помощью опции --color. -1 указывает fzf сохранять исходный цвет из входных данных. См. man fzf для доступных опций цвета.
  • Значение опции --preview-window состоит из пяти компонентов, разделённых запятыми:
    1. up — положение окна предварительного просмотра
    2. 60% — размер окна предварительного просмотра
    3. border-bottom — граница окна предварительного просмотра только снизу
    4. +{2}+3/3 — смещение прокрутки содержимого окна предварительного просмотра
    5. ~3 — фиксированный заголовок
  • Давайте разберём последние два компонента. Мы хотим отображать вывод bat в окне предварительного просмотра с определённым смещением прокрутки, чтобы соответствующая строка была расположена ближе к центру окна предварительного просмотра.
    • +{2} — базовое смещение извлекается из второго токена
    • +3 — мы добавляем три строки к базовому смещению, чтобы компенсировать заголовочную часть вывода bat
      • ───────┬──────────────────────────────────────────────────────────
               │ File: CHANGELOG.md
        ───────┼──────────────────────────────────────────────────────────
           1   │ CHANGELOG
           2   │ =========
           3   │
           4   │ 0.26.0
           5   │ ------
    • /3 корректирует смещение так, чтобы соответствующая строка отображалась на третей позиции в окне.
    • ~3 делает верхние три строки фиксированным заголовком, чтобы они всегда были видны независимо от смещения прокрутки.
  • Вместо использования скрипта оболочки для обработки конечного вывода fzf мы используем действие become(...) (добавлено в fzf 0.38.0), чтобы преобразовать fzf в новый процесс, который открывает файл с помощью vim (vim {1}) и перемещает курсор на строку (+{2}).

Использование fzf в качестве интерактивного запуска Ripgrep

Мы узнали, что можно привязать действие reload к клавише (например, --bind=ctrl-r:execute(ps -ef)). В следующем примере мы будем привязывать действие reload к событию изменения так, чтобы при каждом изменении строки запроса пользователем на fzf действие reload запускалось.

Вот вариация приведённого выше скрипта rfv. fzf будет перезапускать Ripgrep каждый раз при обновлении строки запроса пользователем. Поиск и фильтрация полностью выполняются Ripgrep, а fzf просто предоставляет интерактивный интерфейс. Таким образом, мы теряем "нечёткость", но производительность будет лучше на больших проектах, и освободится память при уточнении результатов.

#!/usr/bin/env bash

# Двухфазная фильтрация с помощью Ripgrep и fzf
#
# 1. Поиск текста в файлах с помощью Ripgrep
# 2. Интерактивный перезапуск Ripgrep с действием reload
# 3. Открытие файла в Vim
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="${*:-}"
fzf --ansi --disabled --query "$INITIAL_QUERY" \
    --bind "start:reload:$RG_PREFIX {q}" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --bind 'enter:become(vim {1} +{2})'

image

  • Вместо обычного запуска fzf в форме rg ... | fzf, мы заставляем его сразу запустить начальный процесс Ripgrep через привязку start:reload для согласованности кода.
  • Фильтрация больше не является обязанностью fzf; поэтому используется опция --disabled.
  • {q} в команде reload оценивается как строка запроса на приглашении fzf.
  • sleep 0.1 в команде reload используется для "дебаунсинга". Это небольшая задержка уменьшит количество промежуточных процессов Ripgrep во время ввода запроса.

Переключение в режим поиска только с помощью fzf

В предыдущем примере мы потеряли возможность нечёткого поиска, полностью делегировав функциональность поиска Ripgrep. Но мы можем динамически переключаться в режим поиска только с помощью fzf путём "отвязки" действия reload от события изменения.

#!/usr/bin/env bash

# Двухфазная фильтрация с помощью Ripgrep и fzf
#
# 1. Поиск текста в файлах с помощью Ripgrep
# 2. Интерактивный перезапуск Ripgrep с действием reload
#    * Нажмите alt-enter для переключения на фильтрацию только с помощью fzf
# 3. Открытие файла в Vim
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="${*:-}"
fzf --ansi --disabled --query "$INITIAL_QUERY" \
    --bind "start:reload:$RG_PREFIX {q}" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --bind "alt-enter:unbind(change,alt-enter)+change-prompt(2. fzf> )+enable-search+clear-query" \
    --color "hl:-1:underline,hl+:-1:underline:reverse" \
    --prompt '1. ripgrep> ' \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --
  • Фаза 1. Фильтрация с помощью Ripgrep image
  • Фаза 2. Фильтрация с помощью fzf image
  • Мы добавили опцию --prompt, чтобы показать, что fzf изначально работает в режиме "запуска Ripgrep".
  • Мы добавили привязку alt-enter:
    1. отвязывает событие изменения, чтобы Ripgrep больше не перезапускался при нажатии клавиш,
    2. изменяет приглашение на 2. fzf>,
    3. активирует функциональность поиска fzf,
    4. очищает текущую строку запроса, использованную для запуска процесса Ripgrep,
    5. и отвязывает саму alt-enter как одноразовое событие.
  • Мы вернули опцию --color для настройки отображения соответствующих фрагментов во второй фазе.

Переключение между режимами Ripgrep и fzf

[fzf 0.30.0][0.30.0] добавил действие rebind, чтобы можно было "перепривязывать" привязки, которые ранее были "отвязаны" через действие unbind.

Это улучшенная версия предыдущего примера, которая позволяет переключаться между режимом запуска Ripgrep (CTRL-R) и режимом фильтрации только с помощью fzf (CTRL-F).

#!/usr/bin/env bash

# Переключение между режимом запуска Ripgrep (CTRL-R) и режимом фильтрации только с помощью fzf (CTRL-F)
rm -f /tmp/rg-fzf-{r,f}
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="${*:-}"
fzf --ansi --disabled --query "$INITIAL_QUERY" \
    --bind "start:reload($RG_PREFIX {q})+unbind(ctrl-r)" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --bind "ctrl-f:unbind(change,ctrl-f)+change-prompt(2. fzf> )+enable-search+rebind(ctrl-r)+transform-query(echo {q} > /tmp/rg-fzf-r; cat /tmp/rg-fzf-f)" \
    --bind "ctrl-r:unbind(ctrl-r)+change-prompt(1. ripgrep> )+disable-search+reload($RG_PREFIX {q} || true)+rebind(change,ctrl-f)+transform-query(echo {q} > /tmp/rg-f zf-r)" \
    --

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-fzf.git
git@api.gitlife.ru:oschina-mirror/mirrors-fzf.git
oschina-mirror
mirrors-fzf
mirrors-fzf
master