Утилита швейцарской армии, которой вам не хватает в вашем наборе инструментов.

Во время работы над проектами машинного обучения я обычно захожу на какой-нибудь сервер по ssh, где терминал — единственный доступный интерфейс. Однако barebone-инструменты могут иметь очень ограниченные возможности, что снижает мою производительность.

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

Настраивать

Во-первых, вам нужно установить fzf с помощью вашего менеджера пакетов или из git. Для других операционных систем ознакомьтесь с разделом установки в документации fzf.

# Conda
conda install -c conda-forge fzf
# Debian 9+ and Ubuntu 19.10+
sudo apt-get install fzf
# Git
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
# macOS
brew install fzf
$(brew --prefix)/opt/fzf/install

После установки вы можете проверить, правильно ли он работает, набрав fzf в своем терминале. Он должен рекурсивно отображать список всех файлов в текущей папке.

На этом этапе вы можете спросить себя, насколько это полезно, поскольку команда возвращает путь к файлу, и все. Итак, давайте рассмотрим несколько вариантов использования.

Применение

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

Основы

Во-первых, давайте рассмотрим некоторые основы взаимодействия с fzf:

  • Чтобы перейти вверх и вниз по списку, используйте <CTRL j/n>и <CTRL k/p>.
  • Как только вы найдете нужный файл, нажмите <ENTER>, чтобы выбрать файл.
  • Для точного совпадения используйте'. Например, 'myfile.
  • Чтобы соответствовать суффиксу, используйте $ . Например, .py$.
  • Чтобы соответствовать префиксу, используйте ^ . Например, ^test_.
  • Чтобы соответствовать нескольким критериям, используйте | . Например, .py$ | .json$.
  • Чтобы выйти, нажмите <CTRL c>.

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

Мы все были в ситуации, когда вы хотите выполнить команду, которую вы использовали ранее, и не можете ее найти. Во-первых, собственная история команд поиска UNIX сложна в использовании. Во-вторых, нажимать клавиши со стрелками 20 раз нецелесообразно. Итак, теперь, когда вы установили fzf, нажмите <CTRL r> в своем терминале и наслаждайтесь!

Изменить каталог

Перемещение может стать очень утомительным при работе над проектом с глубоко вложенной структурой папок, особенно если вы не знакомы с ним. Чтобы использовать fzf для смены каталога, нажмите <ATL c> в своем терминале, затем fuzzy найдет папку, которую вы ищете.

Показать содержимое файла

Допустим, вы хотите отобразить содержимое определенного файла, но не помните точное имя файла и его местоположение. Решением грубой силы было бы cd к группе папок, пытающихся найти его. Другим решением является использование утилиты поиска. Однако, если вы не знаете точное имя файла, вам нужно будет использовать регулярное выражение. Наконец, вы открываете свою IDE, ждете ее запуска, открываете проект, а затем находите файл.

Теперь давайте посмотрим, как будет выглядеть этот рабочий процесс с использованием fzf. В качестве примера я хочу отобразить содержимое myfile.txt, но допустим, что я помню только то, что имя файла содержало my.

Команда, которую я использовал выше, — cat, так как я хотел отобразить содержимое файла. Но вы можете использовать любую команду, которая принимает путь к файлу в качестве аргумента. Кроме того, вы можете добиться тех же результатов, используя $(fzf) или **<TAB> следующим образом:

<YOUR COMMAND> $(fzf)/`fzf`
# or
<YOUR COMMAND> <CTRL t>
# or
<YOUR COMMAND> **<TAB>

Выбор нескольких файлов

Допустим, вы хотите удалить три файла в разных каталогах, но точно не знаете, где они находятся. Судя по тому, что мы видели, решение будет состоять в том, чтобы выбирать и удалять файлы один за другим.

# For file 1
rm <CTRL t>
# For file 2
rm <CTRL t>
# For file 3
rm <CTRL t>

Однако fzf предоставляет функцию множественного выбора с помощью клавиши <TAB>. В приведенном ниже примере я использую множественный выбор для удаления трех файлов в трех разных каталогах.

Фильтрация вывода команды

Во всех приведенных выше примерах мы использовали fzf для передачи аргументов команде (cd, rm и т. д.). Однако fzf также может фильтровать вывод команды, что очень полезно в случаях, когда вывод команды очень длинный. В приведенном ниже примере я использую fzf для поиска в выводе команды ps, чтобы проверить состояние некоторых процессов, используя ps aux | fzf.

Создание собственного

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

Вариант использования

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

Ниже представлена ​​упрощенная структура проекта. train.py — это обучающий скрипт Python, а data — папка, содержащая файлы данных. Кроме того, сценарий обучения принимает один аргумент, представляющий используемый файл данных.

fzf-demo
├─ train.py
├─ data/
├─── dataset_1.jsonl
├─── dataset_2.jsonl
...
├─── dataset_n.jsonl

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

git clone [email protected]:Am1n3e/fzf-demo.git

Список файлов набора данных

Во-первых, нам нужно перечислить доступный файл набора данных, а затем использовать fzf, чтобы выбрать его.

ls data/*.jsonl | fzf

Вывод приведенной выше команды должен быть примерно таким:

В этом примере я перечисляю только локальные файлы с помощью команды ls. Однако вы можете использовать fzf с выводом любой команды или приложения. Например, вы можете перечислить файлы корзины S3.

Добавление предварительного просмотра

Как я объяснил ранее, мне нужно быстро просмотреть файлы (возможно, первые две строки) перед началом обучения. Кроме того, я не хочу открывать весь файл, так как некоторые файлы огромны, и для их открытия может потребоваться время. Наконец, я хочу видеть предварительный просмотр, когда я выбираю файл данных, не выходя из fzf.

Во-первых, я буду использовать head для чтения первых двух строк и jq (см. Здесь для получения дополнительной информации о команде jq) для форматирования вывода. Например, head -n 2 data/dataset_1.jsonl | jq . выведет следующее:

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

ls data/*.jsonl | fzf --preview 'head -n 2 {} | jq .'

Ниже приведен вывод команды выше. Как видите, предварительный просмотр обновляется, когда я выбираю файлы данных.

Значение аргумента --preview может содержать любую команду. Вы даже можете вызвать сценарий, который вы создаете, если предварительный просмотр более сложный. Единственное, что вам нужно помнить, это то, что fzf заменит {} на текущую выбранную строку. В нашем примере это путь к файлу.

Связывание команд

В моем случае я также хочу удалить файлы, содержащие ошибки, без существующего файла fzf. Для этого мы можем связать команду с аргументом --bind следующим образом:

ls data/*.jsonl | \
  fzf \
  --preview 'head -n 2 {} | jq .' \
  --bind 'tab:execute(rm {})'

Здесь я сопоставил <TAB> с запуском rm. Как и аргумент --preview, {} содержит содержимое выбранной строки. Обратите внимание, что вы можете сопоставить более одной команды. Однако существует набор сочетаний клавиш, которые вы можете использовать (подробнее см. man fzf).

Дополнительно

fzf предоставляет несколько параметров для настройки интерфейса, таких как цвета, границы, справка, макет, размер и т. д. Ниже приведены параметры, которые я использую:

ls data/*.jsonl | \
  fzf \
  --preview 'head -n 2 {} | jq .' \
  --bind 'tab:execute(rm {})' \
  --layout=reverse \
  --border \
  --prompt "train_file> " \
  --header $'Press TAB to delete the selected file\n\n'

Подробнее читайте в документации.

Собираем вместе

Теперь, когда у нас есть готовая команда fzf, мы можем использовать ее вывод в качестве аргумента сценария train.py, как показано ниже. Для удобства я создал функцию и добавил ее в свой файл ~/.zshrc или ~/.bashrc.

train() {
    data_file=$(ls data/*.jsonl | \
        fzf \
        --preview 'head -n 2 {} | jq .' \
        --bind 'tab:execute(rm {})'  \
        --layout=reverse \
        --border \
        --prompt "train_file> "  \
        --header $'Press TAB to delete the selected file\n\n') && \
        python train.py $data_file
}

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

Заключение

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

*Все изображения сделаны Амин Эль Хаттами, если не указано иное.

Перед тем, как ты уйдешь

Следите за мной в Твиттере, где я регулярно пишу твиты о разработке программного обеспечения и машинном обучении.