Настройка автодополнения аргументов к скрипту do.sh

14 1

В мои обязанности как системного инженера компании СТАРКОВ Групп входит работа с системой DirectumRX — её настройка на серверах клиентов, где используется операционная система на основе Linux.

Предыстория

Основным инструментом для работы с системой является скрипт do.sh, который принимает аргументы и передает их следующим скриптам, которые уже выполняют определенные действия с системой. 
Количество возможных аргументов у данного скрипта, достаточно много и запомнить их все достаточно сложно.

Можно конечно, но зачем? 

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

А теперь представьте, что если бы не было автодополнения у kubectl? Я думаю, что итог был бы так себе.

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

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

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

Первым делом я объявил функцию do_sh_completion().. Да да, в bash можно делать и такое.

Переменные

Внутри самой функции я объявляю локальные переменные, которые будут использоваться в скрипте:

local cur prev opts sub_opts

Условное обозначение у них следующее:

  • cur: текущий аргумент, который вводит пользователь;
  • prev: предыдущий аргумент в команде;
  • opts: список основные параметры команды;
  • sub_opts: список под-параметров, которые зависят от предыдущего аргумента.

Чуть ниже объявим следующие переменные, где мы определим значения cur и  prev:

COMPREPLY=()  
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
  • COMPREPLY - массив возможных вариантов, которые будут предложены пользователю при нажатии клавиши Tab;
  • COMP_WORDS - массив, который содержит все аргументы, введенные пользователем в командной строке, начиная с ./do.sh;
  • COMP_CWORD - индекс текущего аргумента в массиве COMP_WORDS.

Основные параметры

Основные параметры задаются в переменную opts:

opts="rx generate_data_protection_cert_from_config all ..."

В ней мы указываем аргументы, которые пойдут сразу после вызова скрипта ./do.sh
Это значит, что в контексте команды мы должны получить следующее:

./do.sh <ARG_1>

Как вы знаете, сюда мы можем указать различные аргументы, например all или dt.
Полный список аргументов мы можем получить при вызове do.sh

Идем дальше.

Под-параметры

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

case "${prev}" in  
    ... 
    platform)  
        sub_opts="up down start stop restart config_up check"  
        ;;  
    components)  
        sub_opts="list add add_package delete delete_all"  
        ;;  
    db)  
        sub_opts="up convert check"
        ;;
    ...
esac

Выглядит возможно для кого-то страшно и не совсем понятно, но на самом деле, эту конструкцию можно объяснить буквально парой слов.
${prev} - мы уже говорили за эту переменную. Именно от ее значения будет выполняться следующая выборка аргументов. 

Например если prev=components, это значит что мы передали ./do.sh аргумент components:

./do.sh components

Нажав 2 раза на Tab мы в теории должны получить следующее предложение для ввода в командную строку:

user@localhost:/home/user$ ./do.sh components
add          add_package  delete       delete_all   list

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

Генерация подсказок

Следующим этапом мы должны добавить конструкцию:

if [[ -n ${sub_opts} ]]; then  
    COMPREPLY=( $(compgen -W "${sub_opts}" -- ${cur}) )  
else  
    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )  
fi

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

На этом наша функция уже считается написанной, однако остался последний штрих.

Регистрация функции

Нам необходимо зарегистрировать нашу функцию для скрипта do.sh. Для этого добавляем следующие строчки:

complete -F _do_sh_completion /srv/drx/do.sh  
complete -F _do_sh_completion ./do.sh

Поскольку я часто вызываю скрипт из директории DL, то я также указываю относительный путь. Разумеется, можно настроить alias и указывать только 1 параметр, но это я отдаю на откуп вам.

На этом написание скрипта закончено, осталось его установить. 

Установка скрипта

  1. Добавляем права на созданный скрипт:
    chmod a+x ./auto_compl.sh

     

  2. Добавляем строку в файл .bashrc или .bash_profile пользователя, от лица которого ведется работа с do.sh:
    source /path/to/auto_compl.sh

     

  3. Перезапускаем оболочку bash или выполняем:
    source ~/.bashrc
    # или
    source ~/.bash_profile

     

Результат

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

Вводим команду и нажимаем Tab:

./do.sh co

Видим, что в командной строке подставился нужным нам аргумент:

./do.sh components

Далее нажимаем Tab 2 раза и видим предложения вторичных аргументов:

user@localhost:/home/user$ ./do.sh components
add          add_package  delete       delete_all   list

Можем сделать вывод, что наше автодополнение работает корректно.

Итоги

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

Использование подобных инструментов для работы действительно помогает немного упростить жизнь и сделать её более продуктивной, так почему бы этим не воспользоваться? 

Полное тело скрипта

#!/bin/bash  
  
_do_sh_completion() {  
    local cur prev second_prev opts sub_opts  
    COMPREPLY=()  
    cur="${COMP_WORDS[COMP_CWORD]}"  
    prev="${COMP_WORDS[COMP_CWORD-1]}"  
  
    # Определение основных параметров  
    opts="rx images clean_logs create-update-script generate_data_protection_cert_from_config all dt platform components db rxcmd ct certificatetool webserver nomad_service ccs dos widgets gs indexing intsrv job kds log pss ps reports ss worker wbs wps rabbitmq haproxy mongodb"  
  
    # Определение значений для параметров (если требуется)  
    case "${prev}" in  
        all|webserver|nomad_service|ccs|dos|widgets|gs|indexing|intsrv|job|kds|log|pss|ps|reports|ss|worker|wbs|wps|rabbitmq|haproxy|mongodb)  
            sub_opts="up down start stop restart config_up generate_config_yaml check"  
            ;;  
        platform)  
            sub_opts="up down start stop restart config_up check"  
            ;;  
        components)  
            sub_opts="list add add_package delete delete_all"  
            ;;  
        db)  
            sub_opts="up convert check"  
            ;;  
        rxcmd)  
            sub_opts="import_templates"  
            ;;  
        dt) sub_opts="deploy get_deployed_solutions get_applied_solutions_info init_and_apply_settings init remove_solutions run"  
            ;;  
        ct|certificatetool)  
            sub_opts="run list add remove"  
            ;;  
        enc)  
            sub_opts="encrypt_config decrypt_config"  
            ;;  
        images)  
            sub_opts="remove"  
            ;;  
        rx)  
            sub_opts="install"  
            ;;  
        *)  
            sub_opts=""  
            ;;  
    esac  
    
    if [[ -n ${sub_opts} ]]; then  
        COMPREPLY=( $(compgen -W "${sub_opts}" -- ${cur}) )  
    else  
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )  
    fi  
  
    return 0  
}  
  
complete -F _do_sh_completion /srv/drx/do.sh  
complete -F _do_sh_completion ./do.sh

 

Сергей Корепанов

Добрый день! Возможность включить автодополнение для команд скриптов Directum launcher появилось,  если мне память не изменяет, в версии 4.9. Попробуйте выполнить команду do.sh assign_path <алиас>, перезапустить сессию терминала и можно использовать возможности bash completion. Действует только на добавленные компоненты, после добавления новых компонент надо выполнить команду do.sh reconnect-completion.

Авторизуйтесь, чтобы написать комментарий