Как стать автором
Обновить

Какая команда в консоли отнимает у вас больше всего времени?

Время на прочтение 4 мин
Количество просмотров 24K
У меня — 'cd'.

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

Но должны же быть решения!



В моей любимой оболочке zsh есть такая возможность — «разворачивание» путей по нажатию : например, "/u/in/sy" -> "/usr/include/sys/"

В остальных оболочках можно приноровиться и использовать $CDPATH или pushd/popd, но лично мне это до сих пор кажется неудобным.

А еще есть пара утилиток для ускоренной навигации. Самая известная из них, пожалуй, autojump. Она следит в каких папках пользователь проводит больше всего времени и позволяет указывать только фрагмент пути к папке. Например "incl" приведет вас в "/usr/include", если вы там часто бываете.

Autojump вдохновила другого разработчика на создание утилиты "z". "Z" использует в качестве критерия для перехода т.н. "frecency" - комбинацию частоты посещений папки (frequency) и времени последнего перехода туда (recency).

Обе утилиты хороши по-своему, и я так бы и пользовался autojump или z, однако что-то мне не давало покоя. А недавно я услышал одну фразу:

If the product is used as a tool, its interface should be as unintelligent as
possible. Stupid is predictable; predictable is learnable; learnable is usable.


И тут я понял что самое время придумать свой велосипед. Не-intelligent. Тупой и удобный.

The power of 2



Итак, велосипед называется "2", и он, как и autojump или z, позволяет быстро перейти в нужную папку указывая лишь часть пути.

Состоит "2" из крохотного приложения на языке Go, которое занимается выбором нужной папки, и shell-скрипта, который интегрирует эту утилитку в ваш шелл (bash/zsh).

Разумеется, чтобы определить в какую папку перейти нам понадобится база папок. В отличие от autojump, я не хотел бы учитывать сколько времени провел пользователь в той или иной папке. Да и вообще, все что нужно помнить - это в каких папках пользователь побывал хотя бы раз.

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

В autojump/z использовалась шелл-функция precmd(), которая вызывается всякий раз перед вызовом команды. Нам же подойдет вариант попроще.

Для zsh - это будет функция chpwd(), которая вызывается всякий раз при смене рабочего каталога:

chpwd() {
	$TWO add .
}


Для bash все несколько хитрее:

// делаем функцию с именем cd, теперь она будет вызываться
// вместо команды 'cd'
cd() {
	// внутри функции осуществляем смену папки с помощью встроенной 'cd'
	builtin cd $@
	// и добавляем текущую папку в базу
	$TWO add .
}


Итак, теперь о самой утилите "2". В шелл-скриптах путь к утилите фигурирует под названием $TWO. Если вызвать её с командой "add", то все указанные в качестве параметров папки будут добавлены в базу (если не были добавлены туда ранее).

Если вызвать её с командой "go", то она найдет наиболее подходящую папку по заданным подсказкам и выведет полный путь к папке в stdout. Тогда функция в шелл-скрипте может получить этот путь и выполнить переход в нужную папку. В этой же функции по-особому обрабатываем переход без аргументов (переход в домашнюю папку), и переход по абсолютному пути:

_2go() {
	local path
	[ $# -eq 0 ] && cd && return

	if [ $# -eq 1 -a -d $1 ]; then
		$TWO add $1
		cd $1
		return
	fi
	path=$($TWO go $@)
	[ -z "$path" ] && echo "No matches for '$@'" || builtin cd $path
}
alias 2='_2go'


Есть еще третий режим - "shell", но о нем чуть ниже.

Сама база папок представляет собой обычный текстовый файл $HOME/.2paths, в котором каждая строка - это путь к папке.
Чтение и запись такого файла - задача тривиальная, останавливаться на этом не будем.

Теперь пара слов об алгоритме поиска пути по подсказкам.

Критерий поиска папки



Итак, пользователь вводит подсказки - набор строк. Все эти строки должны встречаться в пути к папке, при чем именно в указанном пользователем порядке.

Однако, что делать если подходят несколько папок?

Когда выполняется поиск утилита запоминает позиции (смещения) подсказок в строке. Сумма этих позиций и определяет своего рода абсолютный вес пути, например для запроса "2 u in":

/usr/include
 ^   ^^
 1 + 5 = 6
/usr/include/wine
         ^    ^^
         10 + 15 = 25


Таким образом, будет выбран путь, у которого подсказки встречаются ближе к правой части. Просто скромный опыт использования autojump/z показал, что в качестве подсказок обычно используешь название (или часть названия) самой последней папки в пути, т.е. под "2 us" подразумеваешь обычно какой-нибудь "/home/serge/src/usb-driver", а не "/usr/include/linux".

Затем вычисляем относительный вес папки, т.е. делим абсолютный вес на длину строки, чтобы папки с разной длиной пути имели равные шансы. Относительные веса в примере выше будут 6/12 = 0.5 и 25/17 = 1.47 соответственно.

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

Может звучит немного запутанно, но в целом - указывайте конец пути, а если не уверены, то пару букв из имен папок верхнего уровня.

А если все равно не уверены?



А есть способ еще проще - интерактивный режим. Идея такова. Вы вводите буквы, которые, как вам кажется, намекают на нужную папку, а на экране видите наиболее подходящий под эту подсказку путь. Нажатие на
 приводит вас в эту папку.
Например, я ввел "trikob" и перейду в "/home/serge/src/trikita/obsqr", если нажму :



Интерактивный режим включается в zsh с помощью <Ctrl-2>, а в bash с помощью <Alt-2>.

Да, я хочу это попробовать!



Исходники открыты и лежат на https://bitbucket.org/zserge/2

Это все еще очень ранняя версия, с багами, порой непродуманным поведением и почти без документации. Так что если что - не стесняйтесь, спрашивайте, советуйте, предлагайте!

Итак, для того чтобы все скомпилировать, сделайте примерно следующие шаги (да, компилятор Go должен быть установлен заранее):

$ hg clone https://bitbucket.org/zserge/2 $ cd 2 $ go build $ sudo cp 2 /usr/local/bin $ sudo mkdir /usr/local/share/2 $ sudo cp zshrc /usr/local/share/2/zshrc $ sudo cp bashrc /usr/local/share/2/bashrc


Если же компилятора Go у вас нет, то возьмите готовые бинарники для 32-битных и 64-битных архитектур. Архивы просто распакуйте в корень файловой системы.

После того как вы установите 2, bashrc и zshrc в нужные места останется только прописать их в вашем шелле. Для этого добавьте последней строкой в конфиге шелла ($HOME/.bashrc или $HOME/.zshrc):

. /usr/local/share/2/bashrc # или zshrc, если ваш шелл - zsh


Этого должно быть достаточно. Перезапустите шелл (откройте новый терминал), и походите немного по папкам (с помощью cd, mc или 2 с указанием полных путей). Это должно создать вашу базу папок в $HOME/.2paths.

И вот теперь вы можете использовать "2" на всю катушку. Легких вам переходов!
Теги:
Хабы:
+71
Комментарии 77
Комментарии Комментарии 77

Публикации

Истории

Работа

Go разработчик
124 вакансии

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн