О чем все это?
Библиотека shell-framework - это набор функций, которые вы можете вызвать из своего скрипта. Она была задумана для облегчения создания скриптов на языке bash и полностью написана на этом языке. В основном библиотека писалась для упрощения работы с опциями командной строки, генерации краткой справки(help-а) скрипта и работы с конфигурационными файлами. В этой статье я постараюсь показать как именно можно упростить написание рутинного кода, связанного с этими задачами, используя shell-framework.
Установка
Процесс установки shell-framework предельно прост. Откройте страничку download проекта и скачайте последнюю версию архива с библиотекой, например, shell-framework_0.22.tar.bz2. Предположим, вы положили ее в текущий каталог, который специально создали для экспериментов. Теперь нужно распаковать библиотеку - например так:
после чего должен появиться подкаталог shell-framework. Скачанный архив вам больше не нужен и его можно удалить.$ tar xjf ./framework_0.21.tar.bz2
Пример использования библиотеки для написания скриптов
Давайте напишем простейший helloWorld скрипт в том самом экспериментальном каталоге и на его примере разберем основные возможности библиотеки:
Вы можете скопировать скрипт прямо отсюда в файл. Я назвал у себя этот файл helloWorld.sh и дальше буду ссылаться на него именно под этим именем. Не забудьте сделать его исполняемым - например так:#!/bin/bash shF_PATH_TO_LIB="./shell-framework/lib" source "${shF_PATH_TO_LIB}/base" setDescription "Hello world is example script." #addOption <name> [defaultValue] [shortForm] [longForm] [type] [shortDescription] [longDescription] [priority] [notForConfig] # type can be shF_SIMPLE_OPTION or shF_OPTION_WITH_VALUE addOption "name" "World" "-n" "--name" "${shF_OPTION_WITH_VALUE}" "you can define your name as ${shF_COMMON_PARAMETER_NAME}." "" "110" if ! initConfig "$@" ; then exit 1 fi echo "Hello, ${name}!"
Рассмотрим отдельные части более подробно:$ chmod +x ./helloWorld.sh
Библиотека shell-framework - это набор bash скриптов, каждый из который, в свою очередь, определяет набор функций. Таким образом, чтобы начать использовать их в скрипте нужно просто подключить нужный файл с помощью инструкции source или ".".Однако, библиотека состоит из нескольких файлов и они тоже должны, в свою очередь, подключить необходимые функции (например функциям работы с опциями командной строки нужны функции работы с ассоциативными массивами). Для этого определяется переменная shF_PATH_TO_LIB, в которой прописывается, где лежат файлы shell-framework/lib, и именно она используется внутри библиотеки, чтобы собрать весь необходимый нам инструментарий. Ее определение является обязательным и оно должно предшествовать source.#!/bin/bash shF_PATH_TO_LIB="./shell-framework/lib" source "${shF_PATH_TO_LIB}/base"
setDescription - первая функция shell-framework, которую мы использовали. Она устанавливает краткое описание нашего скрипта. Позже эта строка будет использована при генерации справки (help-а).setDescription "Hello world is example script."
Первые две строки - это комментарии, чтобы не забыть в каком порядке идут параметры, что они значат и какие значения можно использовать. Последняя строка - непосредственно вызов addOption, которая добавляет новый параметр (option) со следующими атрибутами:#addOption <name> [defaultValue] [shortForm] [longForm] [type] [shortDescription] [longDescription] [priority] [notForConfig]] # type can be shF_SIMPLE_OPTION or shF_OPTION_WITH_VALUE addOption "name" "World" "-n" "--name" "${shF_OPTION_WITH_VALUE}" "you can define your name as ${shF_COMMON_PARAMETER_NAME}." "" "110"
- переменная name, которая будет хранить его значение
- значение по умолчанию: World
- краткая форма -n
- длинная --name
- после него ожидается значение (${shF_OPTION_WITH_VALUE}),
- описание: "you can define your name as ${shF_COMMON_PARAMETER_NAME}."
- При распечатке в help-е список параметров будет отсортирован в соответствии с весом заданным при инициализации - 110.
initConfig "$@" пытается "распарсить" строку параметров в соответствии с конфигурацией, заданной выше. Если произойдет ошибка, скрипт прервет свое выполнение. При выполнении initConfig, среди прочего будет создана переменная name со значением по умолчанию (World), потом опции командной строки будут перебраны и если найдется комбинация -n <value> или --name=<value>, то name получит новое значение (<value>).if ! initConfig "$@" ; then exit 1 fi
Используем значение переменной name и печатаем приветствие.Итак, мы задействовали 3 функции из библиотеки (setDescription, addOption, initConfig) и набросали тестовый скрипт. Давайте теперь посмотрим, что же он в результате умеет?echo "Hello, ${name}!"
Использование скрипта helloWorld.sh
Попробуем запустить скрипт, который мы только что написали, и убедимся, что мы можем передать ему значение для переменной name, как опцию командной строки.
В последнем примере, скрипт нам сообщает, что после параметра (--name) ожидается значение. Дело в том, что shell-framework предполагает, что при использовании "длинных" option будет использована форма --<option name>=<option value>:$ ./helloWorld.sh Hello, World! $ ./helloWorld.sh -n Nick Hello, Nick! $ ./helloWorld.sh --name Nick <current time>: ERROR ./shell-framework/lib/opts.parseOpts:122 The value for option (--name) must be defined.
Однако этим возможности скрипта не ограничиваются - он умеет рассказывать пользователю о себе и как им пользоваться (help):$ ./helloWorld.sh --name=Nick Hello, Nick!
Как видите, скрипт оказывается содержит намного больше чем единственная option, которую мы с вами сначала определили. Причиной тому - использование библиотеки base, которая на самом деле написана, чтобы избежать рутинного перечисления стандартных, на мой взгляд options. Если в вашем случае это не так, то можно обойтись и без base, подключая по отдельности остальные файлы библиотеки.Наша с вами option name, согласно весу 110, располагается в конце этого списка. Над ней две другие option позволяют нам использовать еще одну возможность библиотеки shell-framework - конфигурационные файлы. Давайте рассмотрим их подробнее.Когда мы создаем option, то мы задаем так же и имя переменной. Порядок инициализации этой переменной будет следующим:$ ./helloWorld.sh -h Hello world is example script. Usage: ./helloWorld.sh [option(s)] [command] Options: -h|--help print this help --help-options print detailed description of options using -l <parameter>|--shF_logLevel=<parameter> define the current log level (<parameter>). You can use the following number - 0(shF_EVERYTHING), 1(shF_DEBUG), 2(shF_INFO), 3(shF_HIGH), 4(shF_WARN), 5(shF_ERROR). Config name is shF_currentLogLevel. Default value is "2". --logFile=<parameter> define the <parameter> as log stream. You can use stdError, stdOut or file name. Config name is shF_currentLogFile. Default value is "stdError". -c <parameter>|--config=<parameter> define the <parameter> as property file which will be loaded. -p|--print-config print the current configuration. -n <parameter>|--name=<parameter> you can define your name as <parameter>. Config name is name. Default value is "World"
- значение по умолчанию, если оно есть
- значение из конфигурационного файла, если оно есть
- значение командной строки, если оно есть
то вполне можем его использовать:name="Config"
Option -p используется для генерации конфигурационного файла, что может быть удобно, если вы не хотите создавать его с нуля или просто для записи какого-нибудь удачного, но большого набора опций.$ ./helloWorld.sh Hello, World! $ ./helloWorld.sh --config=./config.txt Hello, Config! $ ./helloWorld.sh -c ./config.txt --name=Virens Hello, Virens!
Таким образом, конфигурационные файлы, которые умеет использовать наш скрипт - это обыкновенный текст, в котором значок # начинает комментарии, а пары имя-значение используются для определения значений конкретных переменных.$ ./helloWorld.sh -p #define the current log level (). You can use the following number - 0(shF_EVERYTHING), 1(shF_DEBUG), 2(shF_INFO), 3(shF_HIGH), 4(shF_WARN), 5(shF_ERROR). Default value is "2". #shF_currentLogLevel="2" #define the as log stream. You can use stdError, stdOut or file name. Default value is "stdError". #shF_currentLogFile="stdError" #you can define your name as . Default value is "World". #name="World"
Заключение
Вы безусловно успели заметить, что я не описал options связанные с logging-ом скриптов(см. help выше). Это не единственная из неописанных возможностей библиотеки shell-framework - среди них так есть работа с ассоциативными массивами (да - они есть в bash 4, но в более ранних версиях, увы, этот тип данных отсутсвует), некоторое расширение, позволяющее вешать на trap не одну функцию, а несколько, использование unit тестирования и тд.
"source ${shF_PATH_TO_LIB}/opts" -- хоть переменные в кавычки возьмите...
ОтветитьУдалитьВ целом, от беглого просмотра кода осталось жуткое ощущение.
Да, конечно, кавычки - это правильно. Обычно я это делаю, но здесь почему-то опустил. Исправил.
ОтветитьУдалить"Жуткое ощущение" - это определено не то, на что рассчитывают "писатели кода" :) Будет ли уместным спросить Ваши рекомендации или замечания? Если не хотите здесь, то можно обсудить это в почте.
@Roman Cheplyaka комментирует...
ОтветитьУдалитьВ целом, от беглого просмотра кода осталось жуткое ощущение.
Я за то, чтобы решение работало. Первым делом - самолёты, а красивости потом. У меня сия библиотека работает, поэтому, собственно, решили запостить тут.
И да, хочется больше подробностей.
@Beggy комментирует...
Если не хотите здесь, то можно обсудить это в почте.
Николай, выбрось эту дурную привычку - обсуждение того, что касается поста, идёт в комментах поста, и только после того, как обсуждение выходит за все разумные рамки, оно может перетечь в письма.
Кстати, если кто заметил, это первый гостевой пост. Может быть, блин получился несколько комом, но зато я получил ценный опыт в этом деле.
остались крайне хорошие впечатления. Код самих библиотек, лично для меня запутан, но главное что это все работает. У непросвещённых людей, код такого приложения может привести к ступору :-). Под какой лицензией распространяете честно говоря не нашёл?
ОтветитьУдалить@Roman Cheplyaka: Отчего у вас жуткое впечатление осталось?
ОтветитьУдалитьЛично на мой взгляд код написан очень хорошо. Хоть и местами его тяжеловато читать из-за обилия переменных, как мне кажется. Но отлично написанные комментарии помогает понять сам код, а осмысленные имена переменных и функций, так же облегчают чтение и понимание кода.
Единственный момент, где я на некоторе время встал в ступор, это строки 136 и 137, файла logging, из-за открывающей скобки в case ... esac. Но заглянув в man bash сразу все встало на свои места (я не знал, что в bash так тоже можно).
@Beggy: Спасибо Вам за труд! Очень хороший фрейворк :)
Присоединяюсь к теме лицензии - что-то на сайте её не видно. Надо бы исправить.
ОтветитьУдалитьКроме того, реквестирую deb-пакет, так как установка в том виде, в котором она есть сейчас, не есть хорошо.
насколько я понял help каждый раз генерируется заново это так? Лично моё мнение help у законченного скрипта, это статические данные и каждый раз их генерировать это трата ресурсов и времени 0m2.132s было бы прикольно добавить стандартный ключ генерации хелпа и запись этого хелпа в какой-то стандартный файл, а потом просто чтение из файла этих данных. Второе моё имхо, может библиотеки лучше переписать на перле или питоне, мне кажется что при этом возрастет производительность.
ОтветитьУдалить@zysyl
ОтветитьУдалитьHelp как внешний файл - это возможно неплохая идея, но есть несколько контр возражений: 1) Основная идея этого фреймворка - держать все данные, определения опций и описаний в одном месте. Введение дополнительного файла несколько нарушает эту схему. 2) файл help могут забыть перегенерировать или вообще упустить при переносе скрипта. 3) help используется не настолько часто и время выполнения, на мой взгляд, здесь несущественно.
Задумка этой библиотеки возникла лет 6 назад, когда я написал несколько администрирующих скриптов на perl-е. Я тогда написал нечто подобное и конечно же это работало намного быстрее. :) Сейчас это решение для bash и на bash. Не стану лукавить и отрицать, что при написании существовал и спортивный интерес: "Можно ли это вообще сделать на данном языке?" Но вопрос быстродействия меня никогда не волновал - мне кажется, что если вам нужна скорость, то bash всегда будет в числе последних претендентов. С другой стороны - как вы себе представляете инициализацию bash переменных из perl скрипта? Или речь идет о комбинированное решение - медленные части переписать на perl, а сверху их обернуть bash? - Такого монстра будет нелегко поддерживать. Не говоря уже и о том, что иногда ни perl, ни python на сервере может и не быть.
Но возможно я с самого начала пошел не потому пути рассуждений читая ваше имхо - может вы имели в виду, что нужно полностью написать аналог такой библиотеки для perl? Ну эта задача вполне решаема, но видимо не в ближайшее время.
Что касается скорости, то я планирую в самое ближайшее время написать модуль, который будет использовать возможность bash 4 работать с ассоциативными массивами. Сейчас они эмулируются и работа с такими структурами данных занимает значительное время.
@Angel2S2:
ОтветитьУдалитьКак я уже говорил - вначале стоял вопрос можно ли вообще нечто подобное написать на bash. И пока я писал этот framework я узнал много нового про bash. :)) Кусок, про который вы упоминаете, поразил меня чуть менее того факта, что на bash можно сгененрировать stack trace :))
За framework - не за что. Мне и вправду было интересно его написать и старался это сделать хорошо. Пожалуйста, высказывайтесь что именно вы хотели бы видеть в нем. В ближайшее время я опубликую продолжение этой статьи уже с более конкретным описанием каждой из библиотек, здесь или у себя в блоге.
Лицензия... Ох - о ней я как-то и не думал до сих пор... Полагаю это будет GPL или что-то на нее похожее. Какая лицензия наиболее свободна? Может ли меня кто-нибудь проконсультировать по этому вопросу?
ОтветитьУдалить@virens
Deb-пакет. Ого куда замахнулись :)) Ну давай попробуем. А куда его выложить? Есть ли репозитории таких вещей?
Или речь идет о комбинированном решение - медленные части переписать на perl, а сверху их обернуть bash? - вот это я и имел в виду. Пакеты, наверное на вашем сайте выложить, хотя я не доганяю зачем генерировать пакеты, разве что ради того что-бы все было по феншую дистрибутива. Статью можно частично или полностью распространять, канечно с указанием на оригинал? Будем ждать продолжения статьи :)
ОтветитьУдалитьНасчёт лицензии мне кажется это или Apache License v2.0 или GPL3.
ОтветитьУдалитьЛицензия определена - GPLv3. Все необходимые изменения внесены в сорцы и новый релиз (0.23) выложен на сайт.
ОтветитьУдалить@zysyl: Эту статью, как и статьи на моем блоге можно свободно копировать с указанием источника.
Что касается пакета, то мне кажется этой правильной идеей, так как в Linux-ах существует стандартная модель распространения программ и стоило бы ей следовать. Так что для меня вопрос не "зачем", но "как". Выложить на origo не совсем верная мысль, потому что там нет debian репозитория. В общем ждем ответа гуру :)
@Beggy: Для Debian подобных дистрибутивов можно использовать https://launchpad.net/. Очень многие именно так и делают. А вот для rpm, ebuild и т.п. уже подсказать не могу. Т.ж. я не видел серверов, где есть репозитории сразу для всех или хотя бы нескольких систем.
ОтветитьУдалить@zysyl комментирует...
ОтветитьУдалитькомбинированном решение - медленные части переписать на perl, а сверху их обернуть bash?
Я бы вот за это бил бы по рукам длинной стальной линейкой: если уж изобретать велосипед, то из железа целиком, а не куски из стали, части из алюминия со вставками и соплей с пластилином.
Пример (печальный) из моей тяжёлой жизни: есть симулятор адаптивной оптики (SciAO), сделанный на на угрёбищном SciLab - графика силаба такая жуткая, что лучше б её вообще не было. Однако на силабе только обёртка для: библиотеки Фурье-преобразований на Сях, костылястой утилиты ray-tracing на плюсах, куски утилит работы с матрицами на фортране. Всё очень старое, собирается только у автора (который в довершение ко всему ещё и виндузятник, т.е. собирал весь этот балаган под cygwin). Автор свалил из области и забил на всё. У меня другой фразы для всего этого кроме "кг/ам", простите за мой французский, просто нет.
Так что костыли только из одного материала. Пожалуйста. :-)
Статью можно частично или полностью распространять, канечно с указанием на оригинал?
Лицензия в подвале блога. Она там совсем не зря :-)
@Beggy комментирует...
Что касается пакета, то мне кажется этой правильной идеей
Я в общем был несколько удивлён, что пакета нету. Как бы не все на слаквари...
Так что для меня вопрос не "зачем", но "как".
Рядом с сырцами выложить ссылку на Deb-пакет. Зарегистрироваться на каком-нибудь ресурсе хоть google.code что ли. Там всё и сразу, в одном флаконе - и примитивный багтрекинг, и репозиторий, и downloads.
В общем ждем ответа гуру :)
Не знаю, какого вы гуру ждёте, но я бы не стал изобретать велосипедов и пошёл бы на сорсфорж. Да, его новый интерфейс, конечно, на любителя. Но всё-таки. Или ГуглКод как вариант.
ИМХО, если и делать данный фреймворк, то он должен работать и при таком начале файла '#!/bin/sh'. Т.к. не везде BASH используется, а стандартный shell есть на всех системах.
ОтветитьУдалитьАноним, вы не учитываете, что в разных системах sh работает по разному, в связи с тем, что в большинстве систем это не чистый sh, в символьная ссылка на другой шелл (я не редко видел dash).
ОтветитьУдалитьК тому же в фреймвоке используются контсрукции, которые не возможно (или почти не возможно) реализовать средствами sh.
@Анонимный:
ОтветитьУдалитьНесомненно Вы правы. Но во-первых я использовал некоторые специфичные bash вещи, а во-вторых на большинстве современных серверов он есть. Так что я думаю, что это допустимый компромисс.
После некоторых раздумий и действий был создан репозитарий и пакет shell-framework в нем. Шаги установке репозитория и пакет описаны здесь
ОтветитьУдалитьОтличный фреймворк.
ОтветитьУдалитьОчень хотелось бы видеть deb-пакет с фреймворком в официальном репозитории Debian/Ubuntu.
Спасибо, Константин. Мне правда кажется, что framework довольно сырой еще, чтобы замахиваться на официальный репозиторий :)
ОтветитьУдалитьС Новым Годом :)
ОтветитьУдалитьпо поводу аналогов launchpad.net для rpm - https://build.opensuse.org/
сборка для нескольких дистрибутивов описана тут -
http://en.opensuse.org/Build_Service/cross_distribution_package_how_to