В Debian в bash был добавлен патч, благодаря которому пользователь может написать свою функцию, выполняемую в случае, если введённая пользователем команда отсутствует. В Ubuntu эту фичу использует подсказка command-not-found, заметно тормозящая работу, в то время как можно найти более интересные и полезные возможности применения этого механизма, оставив поиск пакета специализированным программам. Поделюсь своим опытом.
У нашего подразделения есть специальная сеть для тестовых серверов и виртуальных машин: 192.168.20.0/24, и очень часто приходится набирать команды типа
Как следует из предыдущего абзаца, очень часто набираются команды вида:
Возникает естественное желание этот процесс сократить и оптимизировать. Когда серверов было немного, я насоздавал множество хитрых алиасов вроде следующего:
Однако вскоре я понял, что поддерживать список из полусотни alias'ов — не true unix way, и задумался об альтернативах. Вспомнил об опытах вебмастеров эпохи web 1.0 по использованию 404 ошибки для отображения страницы с нужным содержанием, задумался о том, каким образом bash перехватывает вызов неизвестной команды и подменяет её командой поиска нужного пакета… В результате беглого изучения состава пакета command-not-found было выяснено, что используется функция
Остальное оказалось делом техники. В
Любое введённое число от 1 до 255 преобразуется в команду
Поскольку используется довольно сложное регулярное выражение, то для его обработки пришлось использовать perl. Ещё был вариант с
Чтобы для всех хостов в 20 сети подставлялось общее имя пользователя ordinary_user, а для избранных хостов — специальные имена, в ~/.ssh/config я добавил строчки (общие параметры для всех хостов, предваряемые конструкцией
К сожалению, мне не удалось заставить эту функцию обрабатывать также и параметры командной строки: функции command_not_found_handle передаётся только первый позиционный параметр, остальные недоступны. Поэтому для каждого нестандартного хоста придётся либо писать полный вариант команды со всеми параметрами, либо указывать настройки сервера в ~/.ssh/config, подобно указанным выше. Имеются и прочие недостатки в реализации, обсуждаемые, в частности, на сайте smylers hates software.
Однако даже с такими ограничениями открываются новые потрясающие возможности. Думаю, что предложенное мной применение не единственное, и этот пост — не последний на данную тему.
P.S. бонус для дочитавших до этого места: библиотека регулярных выражений perl, где я нашел регэксп для проверки строки на соответствие IP-адресу.
У нашего подразделения есть специальная сеть для тестовых серверов и виртуальных машин: 192.168.20.0/24, и очень часто приходится набирать команды типа
ssh user@192.168.20.xx
, причем в командах различается только последняя цифра. У ограниченного числа серверов нужно указывать другой username
. Реже приходится ходить на сервера в других подсетях (в пределах 192.168.0.0/16); также иногда клиенты открывают нам доступ к своим системам, чтобы мы смогли продиагностировать их проблему и решить ее на месте.Как следует из предыдущего абзаца, очень часто набираются команды вида:
ssh ordinary_user@192.168.20.xx ssh special_user@192.168.xx.yy ssh third_user@ww.xx.yy.zz
Возникает естественное желание этот процесс сократить и оптимизировать. Когда серверов было немного, я насоздавал множество хитрых алиасов вроде следующего:
alias 123='ssh user@192.168.20.123'
Однако вскоре я понял, что поддерживать список из полусотни alias'ов — не true unix way, и задумался об альтернативах. Вспомнил об опытах вебмастеров эпохи web 1.0 по использованию 404 ошибки для отображения страницы с нужным содержанием, задумался о том, каким образом bash перехватывает вызов неизвестной команды и подменяет её командой поиска нужного пакета… В результате беглого изучения состава пакета command-not-found было выяснено, что используется функция
command_not_found_handle
. Она принимает в качестве аргумента введённую пользователем команду, выполняет некие действия и возвращает 127, если ничего нельзя сделать (в таком случае bash выводит стандартное сообщение об ошибке), или любое другое число, если что-то получилось.Остальное оказалось делом техники. В
~/.bashrc
была добавлена функция:command_not_found_handle () { if [[ ! "$1" ]] ; then return 127 fi n="$1" if echo $n| perl -ne 'exit(/^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ ? 0:1)' ; then ip=192.168.20.$n elif echo $n| perl -ne 'exit (/^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ ? 0:1)' ; then ip=192.168.$n elif echo $n| perl -ne 'exit (/^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ ? 0:1)' ; then ip=$n else return 127 fi ssh $ip }
Любое введённое число от 1 до 255 преобразуется в команду
ssh 192.168.20.число
; два числа — в ssh 192.168.число.число
; любой введённый IP-адрес превращается в ssh IP-адрес
. Во всех остальных случаях просто выводится сообщение "command not found"
.Поскольку используется довольно сложное регулярное выражение, то для его обработки пришлось использовать perl. Ещё был вариант с
grep -qP
, но эксперименальная опция -P
(расширенная поддержка perl-овых регулярных выражений) включена в grep не во всех дистрибутивах (например, в Ubuntu 8.04 её нет, а в 8.10 уже есть).Чтобы для всех хостов в 20 сети подставлялось общее имя пользователя ordinary_user, а для избранных хостов — специальные имена, в ~/.ssh/config я добавил строчки (общие параметры для всех хостов, предваряемые конструкцией
Host *
, должны находиться в конце списка):Host 192.168.20.251 User special_user1 Host 192.168.20.252 User special_user2 Host 192.168.20.254 User special_user3 Host * User ordinary_user
К сожалению, мне не удалось заставить эту функцию обрабатывать также и параметры командной строки: функции command_not_found_handle передаётся только первый позиционный параметр, остальные недоступны. Поэтому для каждого нестандартного хоста придётся либо писать полный вариант команды со всеми параметрами, либо указывать настройки сервера в ~/.ssh/config, подобно указанным выше. Имеются и прочие недостатки в реализации, обсуждаемые, в частности, на сайте smylers hates software.
Однако даже с такими ограничениями открываются новые потрясающие возможности. Думаю, что предложенное мной применение не единственное, и этот пост — не последний на данную тему.
P.S. бонус для дочитавших до этого места: библиотека регулярных выражений perl, где я нашел регэксп для проверки строки на соответствие IP-адресу.