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
Этот документ поможет вам ознакомиться с некоторыми примерами, которые познакомят вас с продвинутыми возможностями fzf.
--height
По умолчанию fzf открывается в полноэкранном режиме, но это не всегда удобно. Часто вам нужно видеть текущий контекст терминала при использовании fzf. Опция --height
позволяет открывать fzf ниже курсора в неполноэкранном режиме, чтобы вы могли видеть предыдущие команды и их результаты над ним.
fzf --height=40%
Вы также можете экспериментировать с другими параметрами макета, такими как --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
(См. справочную страницу для просмотра полного списка опций)
Но вы точно не захотите каждый раз повторять --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%
Опция --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
# Справа (50% ширина)
fzf --tmux right
# Слева (40% ширина и 70% высота)
fzf --tmux left,40%,70%
[!TIP] Вы также можете ознакомиться с моими плагинами для tmux, поддерживающими этот макет попапа.
fzf может динамически обновлять список кандидатов с помощью произвольной программы с помощью привязок reload
(документация по дизайну reload
доступна здесь).
Этот пример показывает, как можно настроить привязку для динамического обновления списка без перезапуска 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
(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)'
Пример выше использует два разных сочетания клавиш для переключения между двумя режимами, но можно ли использовать одно сочетание клавиш?
Чтобы сделать так, чтобы сочетание клавиш вело себя по-разному при каждом нажатии, нам нужно:
Следующий пример показывает, как:
$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 {}'
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.
Я знаю, что это много информации, давайте попробуем разобрать код.
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
состоит из пяти компонентов, разделённых запятыми:
up
— положение окна предварительного просмотра60%
— размер окна предварительного просмотраborder-bottom
— граница окна предварительного просмотра только снизу+{2}+3/3
— смещение прокрутки содержимого окна предварительного просмотра~3
— фиксированный заголовок+{2}
— базовое смещение извлекается из второго токена+3
— мы добавляем три строки к базовому смещению, чтобы компенсировать заголовочную часть вывода bat
───────┬──────────────────────────────────────────────────────────
│ File: CHANGELOG.md
───────┼──────────────────────────────────────────────────────────
1 │ CHANGELOG
2 │ =========
3 │
4 │ 0.26.0
5 │ ------
/3
корректирует смещение так, чтобы соответствующая строка отображалась на третей позиции в окне.~3
делает верхние три строки фиксированным заголовком, чтобы они всегда были видны независимо от смещения прокрутки.become(...)
(добавлено в fzf 0.38.0), чтобы преобразовать fzf в новый процесс, который открывает файл с помощью vim
(vim {1}
) и перемещает курсор на строку (+{2}
).Мы узнали, что можно привязать действие 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})'
rg ... | fzf
, мы заставляем его сразу запустить начальный процесс Ripgrep через привязку start:reload
для согласованности кода.--disabled
.{q}
в команде reload оценивается как строка запроса на приглашении fzf.sleep 0.1
в команде reload используется для "дебаунсинга". Это небольшая задержка уменьшит количество промежуточных процессов Ripgrep во время ввода запроса.В предыдущем примере мы потеряли возможность нечёткого поиска, полностью делегировав функциональность поиска 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' \
--
--prompt
, чтобы показать, что fzf изначально работает в режиме "запуска Ripgrep".2. fzf>
,--color
для настройки отображения соответствующих фрагментов во второй фазе.[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 )