Pull to refresh

Угадывание мыслей и выполнение несуществующих команд средствами bash

Reading time3 min
Views2.2K
В Debian в bash был добавлен патч, благодаря которому пользователь может написать свою функцию, выполняемую в случае, если введённая пользователем команда отсутствует. В Ubuntu эту фичу использует подсказка command-not-found, заметно тормозящая работу, в то время как можно найти более интересные и полезные возможности применения этого механизма, оставив поиск пакета специализированным программам. Поделюсь своим опытом.

У нашего подразделения есть специальная сеть для тестовых серверов и виртуальных машин: 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-адресу.
Tags:
Hubs:
Total votes 53: ↑50 and ↓3+47
Comments22

Articles