вторник, 19 июля 2016 г.

Меню с нечётким поиском в консоли


Сегодня будет сказка о утилите fzf (command line fuzzy finder). В основе лежит простая идея: fzf читает строки из STDIN, отображает в виде меню с нечётким поиском в консоли и пишет выбранную строку в STDOUT. Несмотря на, скажем даже, примитивность, такой подход позволяет удобно интегрировать со стандартными утилитами командной строки и делать интересные вещи. Ниже приведу несколько примеров того, что на её основе можно сделать.


 

Поиск файла

Начнём с самого простого примера - найти файл в текущей директории и открыть в nano.

Список файлов без git директории:
find * -type f -not -path '*/.git/*'
Передаём список в fzf и добавляем привью для файлов, в котором отображаем первые 30 строк:
find * -type f -not -path '*/.git/*' | fzf --preview="head -30 {}"
И последним шагом - выбранный путь передаём в nano:
nano $(find * -type f -not -path '*/.git/*' | fzf --preview="head -$LINES {}")
Теперь можно однострочник заворачивать в скрипт и добавлять в PATH. И пользоваться:
Энтузиасты сделали на этом механизме целый файловый менеджер.

 

История команд

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

Для реализации внешним скриптом обойтись не получилось, не вышло добавить результат в строку редактирования, пришлось писать widget для zsh. Для bash он в чистом виде не подойдёт, но переделать не составит труда. Представленный ниже скрипт впишите в ".zshrc":
fzf-history-widget() {
  LBUFFER=$(fc -lnr 1 | fzf --tiebreak=begin)
  zle redisplay
}

zle -N fzf-history-widget
bindkey '^R' fzf-history-widget
Тут:
  • "fc -lnr 1" - возвращает инвертированный список всех ранее введённых команд
  • опция "–tiebreak=begin" для fzf - при сортировке результатов, предпочитать строки с совпадениями ближе к началу
  • всё остальное это специфичный для zsh способ записать результат в строку редактирования и навесить на это горячую клавишу ctrl+R
Выглядит вот так:

 

Закладки

Ещё один пример удобного использования - закладки для перехода в часто используемые директории. Он так же, как и предыдущий написан для zsh и добавляется в ".zshrc":
function fzf-bookmarks-widget() {
  cd $(cat "$HOME/.config/bookmarks.cfg" | fzf --tiebreak=begin --tac | awk '{print $2}')
  zle reset-prompt
}

zle -N fzf-bookmarks-widget
bindkey '^D' fzf-bookmarks-widget
Файл настроек - "bookmarks.cfg" имеет вот такой вид:
menu_item_1: /home/rean/test1
menu_item_2: /home/rean/test2
menu_item_3: /home/rean/test3
Т.е. имя закладки без пробелов и через пробел - путь к директории.

Общая идея скрипта: читаем построчно "bookmarks.cfg", отображаем через fzf, из результата достаём второй столбец с путём к директории, переходим в него. Буквально 1 строка (не считая обвязки для zsh) и менеджер закладок готов. Навешиваем горячую клавишу ctrl+D и пользуемся.

Сюда не сложно добавить операции добавления и удаления новых элементов прямо из zsh. Но по опыту - список изменяется настолько редко, что алиасы для команд забываются, их приходиться искать в исходниках. В итоге быстрее вручную удалить\добавить строчку в конфиге.

 

Что ещё…

Что ещё можно сделать, зависит от вашей фантазии, вот навскидку составленный список:
  • Поиск процесса + "kill -9".
  • Выбор файлов для "git add".
  • Интеграция с менеджерами паролей.
  • Сделать TODO лист.
  • Поиск по истории браузера.
Если лень писать самому, существуют готовые пакеты с плагинами.