Русский
Русский
English
Статистика
Реклама

Кодобред

Консольный менеджер сертификатов для JKSPCKS12

05.02.2021 14:16:53 | Автор: admin

Привет Хабр!

С момента популяризации https, работа с сертификатами теперь есть практически в каждом проекте.

habr-certificatehabr-certificate

Сертификаты, которые любой может сгенерировать сам;
Сертификаты которые выдает местный СА в компании, доверие к которому есть только внутри компании;
Бесплатный публичный let's encrypt
Платный сертификат от крупных центров авторизации с дополнительными услугами, типа проверки компании.

Сертификаты для tls/ssl, сертификаты для авторизации, сертификаты для two-say-tls/ssl - даже в пределах одного небольшого проекта с десятком микросервисов и несколькими тестовыми окружениями - сертификатов набирается уйма.

При работе с множеством сертификатов, их удобно хранить не отдельными файлами, а складывать в хранилища (keystore). Самые популярные keystore форматы на сегодня - JKS (java keystore, который считается legacy с выходом 9-й джавы) и PKCS12. Последний вышел уже около 20 лет назад, и несмотря на некоторую критику, является одним из самых надежных, открытых и популярных форматов, который в скором будущем должен уже полностью вытеснить JKS.

Я много работал в проектах, где основным языком разработки был java, поэтому при работе с keystore основной инструмент для меня это консольная утилита keytool, которая поставляется с JDK. И для большинства популярных действий у меня конечно есть микро-шпаргалка:

Посмотреть список сертификатов:

keytool -list -v -keystore "файл.jks" -storepass "пароль"|grep -P '(Alias name:|Entry type:|Serial number:|Owner:|Valid from:)'

Скопировать сертификат из одного keystore в другой keystore:

keytool -importkeystore -srckeystore "откуда.jks" -srcstorepass "пароль" -destkeystore "куда.jks" -deststorepass "пароль" -srcalias "имя сертификата" [ -destalias "новое имя" ]

Импортировать сертификат в keystore из PEM файла:

keytool -import -file "откуда.pem" -keystore "куда.jks" -storepass "пароль" -noprompt -alias "имя_сертификата"

Экспортировать сертификат из keystore в файл:

keytool -exportcert -v -alias "alias" -keystore "keystore.jks" -storepass "storepassword" -rfc -file "exportedcert.cer"

Но однажды мне пришлось поработать в проекте, где сертификатов оказалось мягко говоря много. Плюс несколько сотен виртуалок и контейнеров, где все сертификаты обновлялись вручную. Перед тем? как подойти к вопросу автоматизации, было необходимо все это разобрать и причесать. Но долгое время руки просто не доходили - с одной стороны были вещи более приоритетные а сертификаты в общем напрягали нечасто. С другой стороны, опыта разгребания такого объема у меня раньше не было, и навскидку казалось, что в лоб задача решается слишком уныло и кропотливо, браться за нее по своей инициативе не хотелось - задача чисто техническая, никакие менеджеры и представители бизнеса такое перед нашей командой не ставили. Но где-то в подсознании желание все это перебрать и привести в порядок крутилось. Изредка я почитывал статьи, просматривал сертификат эксплореры, но все они были под графическую оболочку Windows, а политика безопасности не позволяла ставить в компании неодобренный софт на десктопы.

Возможно, я бы собрался силой воли, помучился бы недельку, сделал бы все стандартными однострочниками+notepad+excel, но тут внезапно у меня случился больничный отпуск, а когда температура меня попустила, а на работу еще можно было не выходить, я случайно вспомнил, что считаю себя спецом по bash.

И пусть я не хватаю лавры автора PIUPIU), но тем не менее встречайте:

Консольный двух-панельный keystore менеджер на Linux shell.

Сперва я написал рабочий вариант на bash, но в какой-то момент подумал и переписал все башизмы в пользу POSIX совместимости. Код стал выглядеть более громоздко, зато скрипт проверенно работает в bash/ksh/dash без проблем.
Если я что-то пропустил - напишите в комментариях, ну или форкайте на здоровье ;)

Также используется sed и grep, по поводу последнего - я не очень уверен, насколько POSIX-совместимо grep -P. Но если что, там несложно переписать на sed.

Некоторое сожаление: привычка работы с java вынудила меня всю работу с хранилищами выполнять именно через keystore, который должен быть доступен в PATH (а может быть стоило разобраться и сделать все через openssl?).

Но давайте к делу. Что умеет менеджер вкратце можно наглядно увидеть на скриншотах.

В одно-панельном режиме:

В двух-панельном режиме:

Для поклонников панельных менеджеров (NC, VC, MC, FAR и др), функциональные клавиши и навигация должны быть интуитивно понятны.

Весьма важным плюсом я считаю, что jks_mgr.sh - это просто шелл скрипт, одним файлом размером 20 килобайт (update: уже 30+ кб) (update2: уже 35+ кб).

Никаких обсфукаций, код максимально простой - проверить на отсутствие закладок может в принципе любой джуниор - то есть такой скрипт можно использовать в любом проекте, не нарушая никаких требований по безопасности, требований по лицензионности, лоялен к производительности, установка не нужна - лишь бы на целевой машине был доступен шелл, keytool, sed и grep.

Что было самое интересное во время разработки:

Автоматическая подстройка панелей под высоту/ширину экрана.

Так как экран у меня перерисовывается почти с каждым нажатием, то менять ширину экрана (например если сидеть через графический ssh клиент) можно в произвольное время - после первого же нажатия любой клавиши экран перерисуется и все адаптируется. Пару вечеров ушло на опции отображения/скрытия столбцов - нужно было высчитать и отладить подстройку для одно-панельного и двух-панельного режима, плюс заголовки и текст считается разными формулами. Вот кусочек для определения насколько надо обрезать Certificate Alias, если экран слишком узкий и затем пример вывода заголовка:

WindowWidth="$(tput cols)"if [ -n "$RFILE" ]; then # two-panel    used=24    [ -n "$SHOW_TYPE" ] && used=$(( $used+34 ))    localWidth=$(( ( $WindowWidth - $used ) / 2 - 1 ))    if [ $localWidth -ne $aliasWidth ]; then        aliasWidth=$localWidth        [ $aliasWidth -lt 1 ] && aliasWidth=1        clear    fi..................................headerWidth=$(( $aliasWidth + 5 ))[ -n "$SHOW_TYPE" ] && headerWidth=$(( $headerWidth + 17 ))printf " store: ${blue}%-$(( $headerWidth ))s${rst}" "$LFILE"printf "| store: ${blue}%-$(( $headerWidth -1 ))s${rst}\n" "$RFILE"printf " %-10s" "Valid to"[ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"printf " %-${aliasWidth}s |" "Alias"printf " %-10s" "Valid to"[ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"printf " %-${aliasWidth}s\n" "Alias"

Определение нажатия функциональных клавиш.

Некоторые специальные keypress могут иметь длину до 4 символов, поэтому просто через read было сделать непросто - он не позволяет вариативно менять длину читаемой строки, но после поиска в гугле и экспериментов c read в bash/dash, это выглядит так:

# Special keypress could take up to 4 charactersread -rsN1 keypressif [ "$keypress" == "$escape_char" ]; then    read -sn1 -t 0.01 k1    read -sn1 -t 0.01 k2    read -sn1 -t 0.01 k3    read -sn1 -t 0.01 k4    keypress=${k1}${k2}${k3}${k4}    unset k1 k2 k3 k4fi

Теперь можно сравнивать $keypress с комбинациями типа '[A' (стрелка вверх), '[B' (стрелка вниз), '[13~' (F3) и так далее. Я не совсем уверен, что мой вариант будет работать везде идеально - поэтому на всякий случай почти все хоткеи продублированы обычными буквами.

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

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

Я догадываюсь, что можно заметно ускорить навигацию - производительность в git-bash ужасна - мне быстрее скопировать keystore на ближайшую виртуалку, поковырять там менеджером и скопировать исправленный keystore назад. Но нужно будет переписывать много вычислений, делать независимую пагинацию для каждой панели, а на Linux все работает отлично, поэтому в текущем варианте скрипт меня устраивает и дойдут ли руки до переделки - не уверен.

В конце нужно написать какой-то умный итог и заключение ну он простой:

Хочешь получить от практики удовольствие - придумай задачу, результат которой будет полезен для тебя.

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

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

P.S. На фоне некоторых проблем с nginx, Wargaming и др., сразу хочу уточнить, что jks_mgr.sh был написан ИСКЛЮЧИТЕЛЬНО в нерабочее время на личном компьютере.

Подробнее..

Пайплайны и частичное применения функций, зачем это в Python

04.09.2020 14:06:16 | Автор: admin


Одно из главных достоинств Python его выразительность. Функциональные средства языка позволяют лаконично описывать преобразования над данными. На мой взгляд в Python не хватает некоторых инструментов, которые помогли бы удобнее описывать преобразования данных и дополнить функциональную составляющую языка, в частности "пайплайны функций" и их частичное применение. Поэтому в этом посте я лью воду о возможности и необходимости данных средств с экспериментами по их реализации. Пришёл во многом за критикой. Приятного чтения!


Кратко о ФП в Python и почему не хватает пайплайнов на примере


В Python из базовых средств есть довольно удобные map(), reduce(), filter(), лямбда-функции, итераторы и генераторы. Малознакомым с этим всем советую данную статью. В целом это оно всё позволяет быстро и естественно описывать преобразования над списками, кортежами, и тд. Очень часто(у меня и знакомых питонистов) то, что получается однострочник по сути набор последовательных преобразований, фильтраций, например:
Kata с CodeWars: Найти


$\forall n \in [a,b] : n=\sum_0^{len(n)} n_i ^ i, \text{ } n_i\text{ - i-й разряд числа n}$


Задачка довольно простая, к сожалению(но к счастью для этого поста), решений лучше чем в лоб нет.


Моё решение:


def sum_dig_pow(a, b): # range(a, b + 1) will be studied by the function    powered_sum = lambda x: sum([v**(i+1) for i,v in enumerate(map(lambda x: int(x), list(str(x))))])    return [i for i in range(a,b+1) if powered_sum(i)==i]

С использованием средств ФП как есть получается скобочный ад "изнутри наружу". Это мог бы исправить пайплайн.


Пайплайны функций


Под сим я подразумеваю такое в идеальном случае (оператор "|" личное предпочтение):


# f3(f2(f1(x)))f1 | f2 | f3 >> xpipeline = f1 | f2 | f3 pipeline(x)pipeline2 = f4 | f5pipeline3 = pipeline | pipeline2 | f6...

Тогда powered_sum может стать(код не рабочий):


powered_sum = str | list | map(lambda x: int(x), *args) | enumerate | [v**(i+1) for i,v in *args] | sum

Как по мне, такой код легче писать и читать. args в целом выглядят чужеродно. В реальности, не лазя в кишки питона получилось сделать такое(далеко от любых идеалов):


from copy import deepcopyclass CreatePipeline:    def __init__(self, data=None):        self.stack = []        if data is not None:            self.args = data    def __or__(self, f):        new = deepcopy(self)        new.stack.append(f)        return new    def __rshift__(self, v):        new = deepcopy(self)        new.args = v        return new    def call_logic(self, *args):        for f in self.stack:            if type(args) is tuple:                args = f(*args)            else:                args = f(args)        return args    def __call__(self, *args):        if 'args' in self.__dict__:            return self.call_logic(self.args)        else:            return self.call_logic(*args)

Естественно, это один большой костыль, состряпанный ради интереса, даже без kwargs, хотя в похожих случаях и не так важно.


pipe = CreatePipeline()powered_sum = pipe | str | list | (lambda l: map(lambda x: int(x), l)) | enumerate | (lambda e: [v**(i+1) for i,v in e]) | sum

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


Частичное применение функций


Рассмотрим на примере простейшей функции(код не рабочий):


def f_partitial (x,y,z):    return x+y+zv = f_partial(1,2)# type(v) = что-нибудь частично применённая функция f_partial, оставшиеся аргументы: ['z']print(v(3))# Эквивалентprint(f_partial(1,2,3))

Такая возможность была бы полезна для пайпа и другого разного(насколько фантазии хватит). Тогда пример с учётом имеющейся реализации pipe может стать таким:


powered_sum = pipe | str | list | map(lambda x: int(x)) | enumerate | (lambda e: [v**(i+1) for i,v in e]) | sum# map будет вызван ещё раз со вторым аргументом# map(lambda x: int(x))(данные) при вызове

map(lambda x: int(x)) в пайплайне выглядит более лаконично в целом и в терминах последовательных преобразований данных.
Кривенькая неполная реализация на уровне языка:


from inspect import getfullargspecfrom copy import deepcopyclass CreatePartFunction:    def __init__(self, f):        self.f = f        self.values = []    def __call__(self, *args):        args_f = getfullargspec(self.f)[0]        if len(args) + len(self.values) < len(args_f):            new = deepcopy(self)            new.values = new.values + list(args)            return new        elif len(self.values) + len(args) == len(args_f):            return self.f(*tuple(self.values + list(args)))

Реализация примера с учётом данного костыля дополнения:


# костыль для обхода поломки inspect над встроенным mapm = lambda f, l: map(f, l)# создаём частично применяемую функцию на основе обычной питоньейpmap = CreatePartFunction(m)powered_sum = pipe | str | list | pmap(lambda x: int(x)) | enumerate | (lambda e: [v**(i+1) for i,v in e]) | sum

При более чем двойном вызове в строке(что в целом не особо нужно), придётся уже расставлять скобки, потому что питон подумает, что вызывается аргумент, то есть:


def f (x,y,z):    return x+y+zf = CreatePartFunction(f)# работаетprint(f(1,2,3))# работаетprint(f(1,2)(3))print(f(1)(2,3))# не работает# 2(3) - int не callableprint(f(1)(2)(3))# работаетprint((f(1)(2))(3))

Итоги


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

Подробнее..

Ищите бесплатные альтернативы cPanel? Держите сразу 7

19.10.2020 10:13:52 | Автор: admin
image

Приветствую вас, уважаемые читатели и почитатели Хабра!
В этой статье я расскажу вам о 7 популярных альтернативах cPanel. Вы узнаете:
Почему бесплатные панели управления web-сервером нередко оказываются лучше, чем платные аналоги
Какие возможности готовы предложить Вам бесплатные панели прямо здесь и сейчас
Какие из этих программ смогут совершить настоящий прорыв уже в ближайшем будущем.

Эту статью я начал готовить неделю назад. Вчера и позавчера она даже мне снилась. Я часто вижу вещие сны, и именно потому вы сейчас читаете эти строки (вот сказал сейчас что-то умное, но до конца не понял, что именно).
Говоря о cPanel, стоит отметить, что ее преимущества всё ещё превышают недостатки. К последним относится её платная основа. Как говорится, не заплатишь не попользуешься. Смотрим тарифы от 15 баксов в месяц! Хм. Многие даже за сервер в месяц платят меньше.

image

А ведь за 11.88 в год вполне реально купить VPS сервера с отличным конфигом 1 Core / 1536MB / 10GB SSD. В AlexHost.com знают об этом не понаслышке. Сервер + альтернативная панель управления и вы получите отличную комбинацию, которая мало чем будет уступать более дорогостоящей cPanel.
Мы не знаем, что предлагают другие. Но точно знаем, что можем предложить мы:

image

По своему опыту могу сказать всегда старайтесь во всем разобраться сами. Наличие знаний во многих вопросах поможет всегда делать верный выбор. Взять хотя бы выбор панели управления. Не зная о них ничего, вы сразу купите cPanel, даже не рассмотрев альтернативы.
Продолжайте читать и подобная участь вам не грозит. Ведь какую бы из 7 представленных панелей вы не выбрали, вам не придется сожалеть о своем решении.
И еще одна приятная новость вместе с подробным обзором, мы дадим вам ссылки на учебные пособия, которые помогут вам начать управлять серверами Linux без лишних затрат.

Как мы выбрали наши альтернативы cPanel:

Выбирая панели, мы брали во внимание 2 фактора:
1. Бесплатность
2. Наличие многофункциональной бесплатной версии

Я абсолютно убежден все панели, о которых я сейчас вам расскажу, просто обязаны быть для вас бесплатными. Платные версии пусть выбирают крупные компании.
Еще одна приятная новость большинство альтернатив cPanel имеют открытый исходный код. Вы можете просматривать и изучать базовый код. Вы даже можете вносить нужные изменения. Разумеется, при наличии у вас необходимых знаний.
Мир FOSS это невероятные возможности для каждого! Опытные и профессиональные разработчики здесь создают шедевры, невероятно функциональные альтернативы известным инструментам. И они совершенно безвозмездно (то есть даром) могут обеспечить доступ к своим разработкам, делая сообщество лучше.

ТОП-7 бесплатных альтернатив cPanel

image

Webmin это сочетание функциональности и удобства. Я считаю эту панель одной из лучших бесплатных альтернатив cPanel.
Знаете, почему? Сейчас я вам расскажу продолжайте читать, это интересно.

Webmin сочетает в себе все возможности cPanel, которые вы получаете бесплатно. Вы сможете:
создавать резервные копии файлов конфигурации благодаря использованию встроенных модулей
устанавливать и настраивать веб-серверы Apache
отслеживать пропускную способность
быстро и просто настраивать fail2ban
устанавливать брандмауэр iptables
администрировать пользователей
настраивать задания cron
защищать соединения SSH

Также вы получите доступ к другим функциям cPanel.
Возможно, внешний вид этой панели придется по душе не всем пользователям, однако этот минус можно исправить, совместив Webmin с Authentic Theme. Благодаря обновлению тем работать с Webmin стало еще приятнее. Когда я использую эту панель, мои глаза практически не напрягаются.

image

Какую операционную систему вы используете Ubuntu, Debian или CentOS? Вне зависимости от вашего ответа, разработчики Webmin предложат вам оптимальный пакет и помогут пройти процедуру установки. Если вы хотите углубиться в подробности, исходный код Webmin доступен на GitHub.

image

CentOS Web Panel это функциональная панель для тех, кто использует CentOS на своем виртуальном частном сервере (VPS), но при этом не считает Webmin лучшим выбором.
Все это о вас? Тогда обязательно присмотритесь к CentOS Web Panel. Я считаю ее вполне удачной альтернативой cPanel.

CentOS Web Panel можно использовать в следующих целях:

для развертывания и администрирования веб-серверов Apache и брандмауэров.
для развертывания баз данных MySQL, обратного прокси-сервера Nginx, электронной почты с самостоятельным размещением.
с целью управления пользователями.
развертывание резервных копий.
отслеживание состояния своей системы через монитор служб.
Одной из уникальных функций CentOS Web Panel является ее автофиксатор. Он сканирует важные файлы конфигурации и пытается автоматически исправить их в случае ошибки.

Для установки вам потребуется:
обновленная установка CentOS
работающий стек LAMP
не менее 1 ГБ оперативной памяти

К сожалению, CentOS Web Panel не обладает полностью открытым исходным кодом, И все же, она по праву считается многофункциональной альтернативой cPanel, которую можно использовать абсолютно бесплатно.

ПРИМЕЧАНИЕ. Веб-панель CentOS официально поддерживается только в CentOS. Если вы используете Debian или Ubuntu, рекомендую поискать другие варианты. Благо, с выбором проблем не возникнет.

image

Ajenti это комплексная панель управления.
Разработчики позиционируют ее как инструмент профессиональных и опытных администраторов, предоставляя быстрый и безопасный способ управления удаленным Linux-модулем.
Совершенно бесплатно, в любое время дня и ночи вы получаете доступ к веб-терминалу, текстовому редактору, файловому менеджеру и прочим инструментам.

Вы получите впечатляющие возможности:
отзывчивый удаленный терминал
установка межсетевых экранов и пакетов
управление пользователями
отслеживание используемых ресурсов
прочие возможности

Ajenti также предоставляет вам несколько плагинов. Имеется возможность добавлять еще больше плагинов или развиваться с Python.
Разработчики говорят:

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

Ajenti подходящая бесплатная панель управления для тех, у кого уже есть несколько служб, работающих на их сервере. Другие панели просто стирают существующие конфигурации. Ajenti, напротив, стремится подобрать вашу текущую конфигурацию, не меняя ее.
Ajenti также позиционируется как заботливая панель.
Хотите знать, почему? Отвечу ни одно из вносимых изменений не приводит к перезаписи файлов, комментариев или параметров.
Это проект с открытым исходным кодом. Найти код вы сможете где? Правильно на GitHub.

image

YunoHost позиционирует себя как серверная операционная система.
Цель проекта Сделать хостинг доступным для всех. Хорошая цель, но подтверждается ли она реальными действиями? Так ли хороша эта панель, как о ней говорят разработчики.
Начнем с того, что если бы эта панель была неважной, ее не было бы в данном рейтинге. Есть ли в ней какие-то продвинутые функции? Тоже нет. Она больше подходит для новичков, чем для профессионалов.
Основная задача этой панели заключается в том, чтобы установить различные приложения, размещаемые самостоятельно.
YunoHost может предложить вам несколько официально поддерживаемых приложений для установки различных программ, таких как Baikal, Nextcloud, WordPress, Zerobin и другие. А вот об обратных прокси-серверах и управлении брандмауэром говорить не приходится.
Все же YunoHost это хороший выбор начинающих пользователей, желающих быстро начать работу с базовыми приложениями.
Вы можете управлять своим VPS через веб-интерфейс YunoHost или командную строку. YunoHost официально поддерживает Debian 8 и кодируется преимущественно на Python под лицензией GPL с открытым исходным кодом. Код доступен на GitHub.

image

Froxlor это панель управления, которая считается легкой альтернативой Webmin.

Разработанная опытными администраторами серверов, эта панель с открытым исходным кодом (GPL) упрощает управление веб сервером.
Вот так красиво написано на официальном сайте. Если говорить о функционале, вам станут доступны:
установка Let Encrypt
настройка PHP
управление MySQL
прочие возможности

У Froxlor есть доступные пакеты Debian и .tar.gz для производственных установок. Официально поддерживается только Debian. Я полагаю, что, при необходимости, можно установить Froxlor и на Ubuntu, но лично я не пробовал этого делать.
Froxlor распространяется по лицензии GPL 2.0 с исходным кодом на GitHub.

image

ISPConfig давний конкурент cPanel. Бесплатный сервис с хорошими возможностями, который я считаю очень перспективным.
Разработчики заявляют о 40000 загрузках в месяц. Внушительные показатели.

image

Пользователи заявляют о том, что ISPConfig это надежная бесплатная альтернатива cPanel, получившая широкую поддержку среди разработчиков с открытым исходным кодом.
Вы можете использовать ISPConfig для:
настройки веб-серверов Apache2 / nginx
настройки почтовых серверов, DNS, зеркалирования
прочих возможностей, доступных при использовании Webmin или Ajenti.

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

Вы можете загрузить файл .targ.gz самостоятельно или следовать учебному пособию Идеальный сервер, чтобы настроить Debian 8, Apache2, BIND, Dovecot и ISPConfig 3.

ISPConfig работает с Debian, Ubuntu и CentOS. Это обеспечивает панели важную опцию гибкость. И неважно, какие приложения вы при этом используете.
Исходный код доступен через GitLab, организации под лицензией BSD с открытым исходным кодом.

image

VestaCP это красиво разработанное ядро панели управления, написанное на Bash. Пуристы Linux точно оценят его по достоинству.

Встроенные функции включают в себя:
развертывание iptables / fail2ban для обеспечения безопасности
развертывание Nginx и / или Apache для веб-серверов
различные почтовые решения
решения для мониторинга, резервные копии и многое другое

С Vesta вы можете работать через командную строку, а не через интерфейс. А это также пусть маленький, но плюсик.
Вы можете использовать VestaCP с CentOS, Debian и Ubuntu, и он лицензируется с GNU.
Исходный код доступен на GitHub.
Примечание. Начиная с лета 2018 года, появились слухи об увеличении объема автоматических атак на серверы VestaCP на основе неизвестных уязвимостей. Распространенные решения включают защиту ваших SSH-соединений с помощью использования и применения ключей и полное отключение пользователя root.
Исключать VestaCP из списка по этой причине, разумеется, не стоит. Однако вам, как потенциальному пользователю, очень важно об этом знать.

image

Выводы

Итак, уважаемые читатели Habr, вы могли убедиться не cPanel единым. Альтернативы есть, причем бесплатные и довольно неплохие.
Webmin функциональная и удобная панель во всех отношениях
CentOS Web Panel подойдет для тех, кто работает на своем виртуальном частном сервере (VPS). Наличие автофиксатора
Ajenti круглосуточный доступ к веб-терминалу, текстовому редактору, файловому менеджеру и огромному количеству других инструментов
YunoHost подходит для новичков, желающих быстро начать работу с базовыми приложениями
Froxlor в значительной степени упрощает управление платформой хостинга и дает доступ ко многим возможностям совершенно бесплатно
ISPConfig панель, которая в скором будущем может стать настоящим прорывом. 40000 загрузок в месяц.
VestaCP предоставляет решения для мониторинга, резервные копии, почтовые решения. Достойная бесплатная альтернатива cPanel
Какую бы бесплатную альтернативу вы не выбрали, у каждой из них будут свои преимущества. И все же, важно понимать, что бесплатные панели никогда не заменят платную cPanel на все 100%. Я рассказал все, что знал, а дальше выбор за вами.

Немного рекламы
Мощные сервера i7-9700 DDR4/16 GB RAM/ 1 TB HDD всего за 70 euro/месяц ТУТ
Подробнее..

Локализация своих скриптов на BASH

05.02.2021 14:16:53 | Автор: admin

Создание меню на BASH задача сама по себе не сложная: "case тебе в руки и echo в спину". Решая её в очередной раз, мне захотелось добавить возможность отображать текст на других языках. Осталось решить, как сделать сам процесс локализации меню более удобным. Если оно большое, то решение "в лоб" превратит его в громоздкую копипасту. Здесь я хотел бы поделиться тем, как решил эту проблему для себя. Надеюсь для кого то это будет не безынтересным.



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


Примечание:

Я вполне понимаю и принимаю, что существуют и другие языки программирования. Как когда-то кто-то сказал здесь на Хабре если при написании скрипта на BASH возникает необходимость хоть в одной функции, то лучше взять нормальный язык. Я с этим согласен, но иногда, как говорится, хочется, потому что хочется.


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


Реализация будет состоять из:


  • буквенного кода языка
  • массива слов
  • преобразователя косвенных ссылок для обращения к массиву
  • обращения к элементам массива
  • создания меню

Теперь рассмотрим подробнее


Зададим язык, добавив короткий буквенный код языка (ru, en), для начала английский:


langset=en

Добавим массивы слов и предложений (по количеству языков):


language_en=( "English" "Quit" "Hi, Habr!" )language_ru=( "Русский" "Выход" "Привет, Хабр!" )

Создадим косвенную ссылку и сформируем из неё новый массив в переменной lng:


lng="language_$langset[@]"; lng=("${!lng}")

То есть здесь для переменной lng создаётся значение, состоящее из части имени массива со словами и кода заданного языка из переменной langset. Далее создаётся массив, который при langset=en будет равен language_en, а при langset=ru будет равен language_ru.
Если языков будет много, то такой подход позволит избавиться от многочисленных if-elif или case. Чтобы изменить язык, достаточно будет добавить массив с переводом и установить язык в переменной langset.


Запустим всё это в консоли:


language_en=( "English" "Quit" "Hi, Habr!" )language_ru=( "Русский" "Выход" "Привет, Хабр!" )langset=enlng="language_$langset[@]"; lng=("${!lng}")echo "${lng[2]}"# Вывод: Hi, Habr!langset=rulng="language_$langset[@]"; lng=("${!lng}")echo "${lng[2]}"# Вывод: Привет, Хабр!

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


Построение меню


А теперь попробуем создать скрипт.
Для примера я взял меню управления командами Git через CLI (command line interface). Это немного надуманно, но хорошо подходит для примера, так как в Git много команд и как раз можно построить единое по своей задаче меню с множеством параметров.


Для начала задаем язык и массив с пунктами меню как это уже делали выше. Ради удобства, я разделил массив на две части: первая это непосредственно меню, вторая информационные сообщения и приглашения командной строки, которые необходимо будет вывести по ходу работы.


#!/bin/bash# Код языкаlangset=en# Пункты менюlanguage_en=( "English" "Quit" "Main menu" "Git: add ALL files/commit" "Git init" "Change language" "Language selection" )message_en=( "English" "Select item" "Wrong! This item does not exist" "Added all files" "Enter you commit" "Changes recorded" "Select a language" "The language has been changed to" "Start the program again" "Menu for language change" )language_ru=( "Русский" "Выход" "Основное меню" "Git: добавить ВСЕ файлы/коммит" "" "" "Выбор языка" )message_ru=( "Русский" "Выберите пункт" "Неверно! Этого пункта не существует" "Добавление всех файлов" "Введите ваш коммит" "Изменения зарегистрированы" "Выберите язык" "Язык изменен на" "Запустите программу заново" "Меню для смены языка" )language_de=( "Deutsch" )message_de=( "Deutsch" "" "" "" "" "" "" "" "Starten Sie das Programm neu" )

Пройдемся по массивам и создадим новые для lng и msg, которые и будут использоваться:


languages() {    lng="language_$langset[@]"; lng=("${!lng}")    msg="message_$langset[@]"; msg=("${!msg}")    for b in ${!language_en[@]} ${!message_en[@]} ; do        if [[ ! ${lng[$b]} ]] ; then            lng[$b]=${language_en[$b]}        fi        if [[ ! ${msg[$b]} ]] ; then            msg[$b]=${message_en[$b]}        fi    done}languages

Чтобы при необходимости иметь возможность применять языковые настройки (это пригодится в следующей статье), заключим всё в функцию и тут же вызовем её.


Небольшое отступление: я уверен, почти всем встречались наполовину переведённые приложения. Особенно этим грешат кастомные прошивки для телефонов. Мне никогда это не нравилось, но в данном случае я всё же решил предусмотреть такую возможность неполного перевода. Это позволит при добавлении нового языка не дублировать все элементы массива, а добавить только те, которые пользователю хотелось бы перевести.


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


Теперь, когда с языками разобрались, приступим к построению меню. Тут всё стандартно: бесконечный цикл, внутри него цикл для вывода элементов на экран и выбор вариантов через case.


Формируем массив для вывода на экран. Только те пункты, которые нам нужны:


local menu0=([1]="${lng[3]}" "${lng[4]}" "${lng[5]}" "${lng[1]}")

Здесь делаем индексацию с 1, чтобы выводить индекс как соответствующий пункт меню.


Добавляем шапку, пробегаемся по индексам через ${!menu0[@]} и выводим на экран:


echoecho "---${lng[2]}---"for op in "${!menu0[@]}" ; do     echo "$op ) ${menu0[$op]}"doneecho ---------- 

Далее предложим пользователю выбрать необходимый пункт. Будем ожидать нажатие цифровой клавиши через read -s -n1 -p. Где -s не отображать введённые данные (чаще используется для ввода паролей). Мы же потом сами отобразим введеный текст в более удобном формате. -p строка приглашения для вывода подсказки. -n1 параметр, считывающий число симмволов ввода. Здесь мы заранее знаем, что пунктов меню будет не более 9 (то есть числа из одной цифры), поэтому при нажатии одного любого символа программа продолжит работу дальше. Не надо будет нажимать Enter для ввода. Немного непривычно, поэтому можно убрать.


# read со строкой приглашенияread -s -n1 -p "${msg[1]}: " item#вывод нажатой клавишиecho "[$item]->: ${menu0[$item]}"

Ну и заключительный оператор выбора case с обработкой введенного значения:


case $item in        # Команды Git, без обработки ошибок (репозитория ведь может и не быть)    1 )     git add .        read -p "${msg[4]}: " comm        git commit -m "$comm"        echo "${msg[5]}" ;;    2 ) git init ;;    3 ) echo "${msg[9]}" ;;        # Выход    4 ) exit ;;        # Обработка остальных клавиш и вывод сообщения об ошибке    * ) echo "[$item]->: ${msg[2]}"; sleep 2 ;;esac        

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


Весь код
#!/bin/bash# Код языкаlangset=ru# Меню и сообщенияlanguage_en=( "English" "Quit" "Main menu" "Git: add ALL files/commit" "Git init" "Change language" "Language selection" )message_en=( "English" "Select item" "Wrong! This item does not exist" "Added all files" "Enter you commit" "Changes recorded" "Select a language" "The language has been changed to" "Start the program again" "There will be a menu for changing the language" )language_ru=( "Русский" "Выход" "Основное меню" "Git: добавить ВСЕ файлы/коммит" "" "" "Выбор языка" )message_ru=( "Русский" "Выберите пункт" "Неверно! Этого пункта не существует" "Добавление всех файлов" "Введите ваш коммит" "Изменения зарегистрированы" "Выберите язык" "Язык изменен на" "Запустите программу заново" "Здесь будет меню для смены языка" )language_de=( "Deutsch" )message_de=( "Deutsch" "" "" "" "" "" "" "" "Starten Sie das Programm neu" )languages() {    # Косвенные ссылки и создание нового массива    lng="language_$langset[@]"; lng=("${!lng}")    msg="message_$langset[@]"; msg=("${!msg}")    # Сравнение массивов для проверки на пропущенные элементы    for b in ${!language_en[@]} ${!message_en[@]} ; do        if [[ ! ${lng[$b]} ]] ; then            lng[$b]=${language_en[$b]}        fi        if [[ ! ${msg[$b]} ]] ; then            msg[$b]=${message_en[$b]}        fi    done}languagesmain() {    # Создание и вывод меню на экран    local menu0=([1]="${lng[3]}" "${lng[4]}" "${lng[5]} [$langset]" "${lng[1]}")    while true ; do         echo        echo "---${lng[2]}---"        for op in "${!menu0[@]}" ; do             echo "$op ) ${menu0[$op]}"        done        echo ----------        # Ожидание ввода значения        read -s -n1 -p "${msg[1]}: " item        echo "[$item]->: ${menu0[$item]}"        # Оператор выбора        case $item in                # Команды Git, без обработки ошибок (репозитория ведь может и не быть)            1 ) #               git add .                read -p "${msg[4]}: " comm#               git commit -m "$comm"                echo "${msg[5]}" ;;            2 ) #               git init ;;            3 ) echo "${msg[9]}" ;;                # Выход            4 ) exit ;;                # Обработка остальных клавиш и вывод сообщения об ошибке            * ) echo "[$item]->: ${msg[2]}"; sleep 2 ;;        esac                done}mainexit 0

Это первая статья из двух. В следующей рассмотрим создание многоуровнего меню, разукрасим вывод (как же без этого), а также попробуем сохранить настройки, то есть задать код языка прямо из меню, не открывая Shell-скрипт в текстовом редакторе.


Спасибо, что дочитали до конца. Надеюсь, было не скучно :)

Подробнее..

Локализация своих скриптов на BASH, часть 2

13.02.2021 16:10:43 | Автор: admin

Это продолжение статьи Локализация своих скриптов на BASH. В ней мы используя массивы и косвенные ссылки, научились добавлять в свои скрипты дополнительные языки и переключаться между ними.
В этой статье составим список встроенных языков и зададим выбор языка через меню, построив для этого многоуровневое меню. Чтобы статья не превратилась в один большой кусок кода с описанием каждой строчки, сам код с подробными комментариями я выложу ниже, а здесь затрону только несколько основных моментов.

Создание меню

В прошлой статье для вывода меню на экран и выбора необходимого пункта мы использовали цикл for и оператор выбора case. Очевидно, что для создания многоуровнего меню эти шаги нужно будет повторить несколько раз, то есть для каждого подменю. При этом придется заново создавать шапку меню, внешний вид и так далее.
Хороший способ этого избежать - вынести for, case и read в отдельную функцию (назовём её prints), а далее будем просто передавать в неё необходимые параметры. Все сценарии, которые будут выполнятся при выборе тех или иных пунктов меню также будут вынесены в соответствующие функции.

Так, чтобы добавить в скрипт новое действие:

  • добавляем в языковой массив слова и фразы

  • в массив с основным или дополнительным меню вставляем соответствующий пункт и команду вызова функции

  • добавляем функцию с необходимым фрагментом кода

Для начала создадим основное меню, которое сразу появится на экране при запуске скрипта:

menu0=("${lng[3]};main" "${lng[4]};gitadd" "${lng[5]};gitinit" "${lng[2]};options" "${lng[1]};exit")

Здесь каждый элемент массива задается двумя параметрами: словесное название необходимого пункта меню, а через разделитель ;- имя функции, которая будет вызвана. Сделал это для удобства, чтобы не создавать лишний массив для списка команд. Там, куда это все будет передаваться, элемент разделится на две части.

Поместим массив в функцию main:

main() {    # Массив с основным меню    local menu0=("${lng[3]};main" "${lng[4]};gitadd" "${lng[5]};gitinit" "${lng[2]};options" "${lng[1]};exit")    while true ; do        # Передаем массив в функцию вывода на экран        # Вторым аргументом идет сообщение, которое отобразится в строке приглашения        prints "menu0[@]" "${msg[1]}"    done}main

При таком подходе создание дополнительных меню немного упрощается, например options:

options() {    local menu1=("${lng[2]};options" "${lng[7]} [$langset];langmenu" "${lng[1]};exit")    prints "menu1[@]" "${msg[1]}"

Теперь пришло время рассмотреть функцию prints, которая выводит все эти меню на экран. Сначала поместим в неё конструкцию, разделяющую элемент на две части. Для разделения задействуем команду вырезания данных cut:

if [[ "$1" == "text" ]] ; then    # Для текста меню до разделителя    echo "$2" | cut -d ";" -f 1    returnelif [[ "$1" == "command" ]] ; then    # Для команды после разделителя    echo "$2" | cut -d ";" -f 2    returnfi

Для получения, например, текстового поля вызывать её будем командой ${prints "text" "${menu[0]}"} , где второй аргумент - сам элемент массива.

Небольшое, но важное отступление: В скрипт я добавил возможность раскрашивать вывод на экран в разные цвета. Отвечающий за это код я поместил в функцию colors. Для раскрашивания используются ANSI escape последовательности (вывод echo -e) с расширенной палитрой на 256 цветов.

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

Я не буду описывать эти функции, но они будут в конечном коде с подробными комментариями.

Продолжим рассматривать функцию вывода меню на экран prints. Задаем массив из массива, переданного в функцию через аргумент:

local menu=("${!1}")

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

pwdscolors "title" "---$(prints "text" "${menu[0]}")---"

Перебираем в цикле for массив, выводим пункты белым цветом, ожидаем ввод значения read и обрабатываем нажатие через оператор выбора case:

for (( op=1; op < "${#menu[@]}"; op++ )); docolors "item" "$op ) $(prints "text" "${menu[$op]}")"doneecho ----------read -s -n1 -p "$(colors "item" "$2: ")" itemcase $item in[1-$((${#menu[@]}-1))] ) # Вывод выбранного пункта меню зеленым цветомcolors "ok" "[$item]->: $(prints "text" "${menu[$item]}")"# Вызов функции с фрагментом кода$(prints "command" "${menu[$item]}") ;;# Немедленное завершение по [q]"q" ) echo; exit;;# Обработка остальных клавиш и вывод сообщения об ошибке красным цветом* ) colors "err" "[$item]->: ${msg[2]}"; sleep 2 ;;esac
Так будет выглядеть это менюТак будет выглядеть это меню

Построение меню выбора языка

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

Итак, для того, чтобы выбирать язык через меню, составим список встроенных языков. Сначала создадим новую функцию langmenu.

Заранее хочу отметить, что мы не знаем количество и какие языки присутствуют в скрипте. Мы просто ищем их все. Для этого пробегаемся по текущему скрипту, через sed и регулярное выражение находим все имена массивов language_ и добавляем коды языков в массив. То есть из language_ru выкусываем ru:

# [-r] - расширенный синтаксис регулярных выражений# [-n] - вывод только того, что совпадает с шаблономlocal lng_sfx=($(sed -r -n "s/^\s?+language_(\w+)=.*/\1/p" "${0}"))

Учитывая, что мы не знали какие языки у нас будут, мы не могли сразу задать массив для передачи его в prints. Нужно будет собрать его по частям. Сделаем это там же в langmenu:

langmenu(){    local lng_sfx=($(sed -r -n "s/^\s?+language_(\w+)=.*/\1/p" "${0}"))    local menu2=("${lng[7]};langmenu")    for a in ${lng_sfx[@]} ; do        local d="language_$a[@]"; d=("${!d}")        menu2+=("$d;languages set $a")    done    menu2+=("${lng[1]};exit")    prints "menu2[@]" "${msg[6]}"}

Здесь мы:

  1. Создаем массив для вывода меню

  2. В цикле перебираем массив с кодами языков. На каждой итерации создаем косвенную ссылку, чтобы обратиться к 0 элементу соответствующего языкового массива (с записанным нзванием языка). На следующем шаге мы формируем элемент меню: в каждый элемент в текстовую часть добавляем название языка, а через разделитель ;, в командную часть, добавляем команду вызова функции languages и в качестве аргумента ставим код языка. Для английского языка получится "English;languages set en", где set en - аргументы для функции languages.

  3. После цикла добавляем в массив с меню команду выхода

  4. Передаем сформированный массив с языковым меню в функцию вывода на экран prints. Вторым аргументом идет сообщение, которое отобразится в строке приглашения

Меню выбора языкаМеню выбора языка

Сохранение языка через настройки

После выбора языка через меню запомним его через перезапись в скрипте. применим для этого потоковый редактор sed с ключами -i и -r, где -i - редактирование (перезапись) файла, -r - поддержка расширенного синтаксиса регулярных выражений.
Здесь всё просто: ищем первое вхождение строки, начинающейся с langset= и в ней переменную langset= с кодом языка через редактирование перезаписываем новым значением и сразу выходим:

sed -i -r "0,/^\s?+langset=/s/langset=[\"\']?\w*[\"\']?/langset=\"$langset\"/" "${0}"exit

В предыдущей части мы сформировали языковое меню и передали его в функцию prints. Которая, в свою очередь, через оператор выбора case вызывает функцию languages, описанную в прошлой статье и передаёт в неё аргумент set и код выбранного языка. Самое время добавить в languages функцию перезаписи настроек, а также вывести на экран сообщение о смене языка в ТЕКУЩЕЙ локализации. После этого применим новый язык и опять выведем то же самое сообщение на НОВОМ языке:

Функция languages
if [ "$1" == "set" ] ; then# Устанавливаем новый язык из входного аргументаlangset="$2"local df="language_$langset"echo# Сообщение на ТЕКУЩЕМ языке что язык изменен, цвет зеленыйcolors "ok" "${msg[7]} ${!df}. ${msg[8]}"# Применяем настройки языкаlanguages# Сообщение на НОВОМ языке что язык изменен, цвет зеленыйcolors "ok" "${msg[7]} ${lng[0]}. ${msg[8]}"echo# Перезаписываем переменную langset= с кодом языка и выходимsed -i -r "0,/^\s?+langset=/s/langset=[\"\']?\w*[\"\']?/langset=\"$langset\"/" "${0}"exit fi
Установка языкаУстановка языка

Заключение

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

Спасибо, что дочитали до конца.

Весь код с комментариями прикреплен ниже.

Весь код
#!/bin/bash# Код языкаlangset="ru"# Меню и сообщенияlanguage_en=( "English" "Quit" "Options" "Main menu" "Git: add ALL files/commit" "Git init" "Change language" "Language selection" )message_en=( "English" "Select item" "Wrong! This item does not exist" "Added all files" "Enter you commit" "Changes recorded" "Select a language" "The language has been changed to" "Start the program again" "Repository not found\nPlease, select Git init pepository" )language_ru=( "Русский" "Выход" "Настройки" "Основное меню" "Git: добавить ВСЕ файлы/коммит" "" "" "Выбор языка" )message_ru=( "Русский" "Выберите пункт" "Неверно! Этого пункта не существует" "Добавление всех файлов" "Введите ваш коммит" "Изменения зарегистрированы" "Выберите язык" "Язык изменен на" "Запустите программу заново" "Репозиторий не найден\nПожалуйста, инициализируйте репозиторий, выбрав Git init" )language_de=( "Deutsch" )message_de=( "Deutsch" "" "" "" "" "" "" "" "Starten Sie das Programm neu" )language_cn=( "" "" "" "")message_cn=( "" "" "" "" "" "" "" "" "" )# Settings sectionlanguages() {# Функция с языковыми настройками и установкой нового языка# Косвенные ссылки и создание нового массиваlng="language_$langset[@]"; lng=("${!lng}")msg="message_$langset[@]"; msg=("${!msg}")# Сравнение массивов для проверки на пропущенные элементыfor b in ${!language_en[@]} ${!message_en[@]} ; doif [[ ! ${lng[$b]} ]] ; thenlng[$b]=${language_en[$b]}fiif [[ ! ${msg[$b]} ]] ; thenmsg[$b]=${message_en[$b]}fidone# Установка нового языкаif [ "$1" == "set" ] ; then# Устанавливаем новый язык из входного аргументаlangset="$2"local df="language_$langset"# Выводим сообщение на ТЕУЩЕМ языке что язык изменен,# пишем какой выбрали, предлагаем перезапустить программуechocolors "ok" "${msg[7]} ${!df}. ${msg[8]}"# Применяем настройки языкаlanguages# Выводим сообщение на НОВОМ языке что язык изменен# пишем какой выбрали, предлагаем перезапустить программуcolors "ok" "${msg[7]} ${lng[0]}. ${msg[8]}"echo# Через регулярное выражение путем изменения файла# перезаписываем переменную langset= с кодом языка и выходим# [-r] - расширенный синтаксис регулярных выражений# [-i] - редактирование файла# [0,] - только первое вхождениеsed -i -r "0,/^\s?+langset=/s/langset=[\"\']?\w*[\"\']?/langset=\"$langset\"/" "${0}"exit fi}# Применяем настройки языкаlanguagescolors() {# Установка цвета текста и фона. Строки даны полностью,# чтобы можно было просто изменить цифры, ничего не дописывая# Здесь [48] - код расширенной палитры фона, [38] - текста# [5] - 8-битный формат цвета (0-255), [1] - жирный,# [22] - отменить жирный, [0] - сбросить все измененияcase "$1" in# Текст: темно-зеленый (часы)"tm" ) echo -e "\e[48;5;256;38;5;34;22m$2\e[0m" ;;# Фон: светло-синий, текст: белый жирный (часть полного пути)"pt" ) echo -e "\e[48;5;24;38;5;15;1m$2\e[0m" ;;# Текст: светло-желтый жирный (текущая папка)"cf" ) echo -e "\e[48;5;256;38;5;226;1m$2\e[0m" ;;# Текст: темно-зеленый жирный (цвет успешной операции)"ok" ) echo -e "\e[48;5;256;38;5;34;1m$2\e[0m" ;;# Текст: красный жирный (цвет ошибки)"err" ) echo -e "\e[48;5;256;38;5;160;1m$2\e[0m" ;;# Текст: светло-желтый (шапка меню)"title" ) echo -e "\e[48;5;256;38;5;226;22m$2\e[0m" ;;# Текст: белый (пункты меню и строка приглашения)"item" ) echo -e "\e[48;5;256;38;5;15;22m$2\e[0m" ;;esac}pwds() {# Цветное отображение полного пути текущей директории и датыecho echo ----------echo "$(colors 'tm' "[$(date +"%T")]") $(colors 'pt' "${PWD%/*}"/)$(colors 'cf'  "$(basename   "$PWD")")"echo ----------}prints() {# Функция вывода меню на экран# Разделение элемента массива на текст и команду, в качестве разделителя [;]if [[ "$1" == "text" ]] ; thenecho "$2" | cut -d ";" -f 1returnelif [[ "$1" == "command" ]] ; thenecho "$2" | cut -d ";" -f 2returnfi# Задаем массив из массива, переданного в функцию через аргументlocal menu=("${!1}")# Вывод даты и текущего путиpwds# Вывод названия меню желтым цветом, название берется# из текстовой части 1 элемента массива colors "title" "---$(prints "text" "${menu[0]}")---"# Вывод меню на экранfor (( op=1; op < "${#menu[@]}"; op++ )); do# Вывод пунктов меню белым цветом, названия берутся# из текстовой части соответствующего элемента массиваcolors "item" "$op ) $(prints "text" "${menu[$op]}")"doneecho ----------# Ожидание ввода значения, приглашение выводится белым цветомread -s -n1 -p "$(colors "item" "$2: ")" item# Оператор выбораcase $item in# Все числа от 1 до размера всего массива минус 1 (так как индексация массива с 0)# Вывод выбранного пункта меню зеленым цветом название берется# из текстовой части соответствующего элемента массива[1-$((${#menu[@]}-1))] ) colors "ok" "[$item]->: $(prints "text" "${menu[$item]}")"# Вызов функции с фрагментом кода, имя функции берется# из командной части соответствующего элемента массива$(prints "command" "${menu[$item]}") ;;# Немедленное завершение по [q]"q" ) echo; exit;;# Обработка остальных клавиш и вывод сообщения об ошибке красным цветом* ) colors "err" "[$item]->: ${msg[2]}"; sleep 2 ;;esac}# Application sectiongitinit() {# Для примера: фрагмент кода для [git init]git init}gitadd() {# Для примера: фрагмент кода для [git add] - добавить все файлыgit add .# Обработка ошибок. Если статус завершения команды не равен [0]# вывести сообщение об ошибке красным цветом и вернуться в менюif [[ "$?" != "0" ]] ; thencolors "err" "${msg[9]}" sleep 1return 1 fiecho "${msg[3]} ..."# Приглашение и ввод коммитаread -p "$(colors "item" "${msg[4]}: ")" commgit commit -m "$comm"# сообщение о завершении операции зеленим цветомcolors "ok" "${msg[5]}"}# Menu sectionlangmenu() {# Функция создания языкового меню# Проходим по текущему скрипту, через регулярное выражение находим# все имена массивов [language_*] и добавляем коды языков в массив# [-r] - расширенный синтаксис регулярных выражений# [-n] - вывод только того, что совпадает с шаблономlocal lng_sfx=($(sed -r -n "s/^\s?+language_(\w+)=.*/\1/p" "${0}"))# Создаем массив для вывода менюlocal menu2=("${lng[7]};langmenu")# Перебираем в цикле массив с кодами языков, на каждой итерации создаем косвенную ссылку,# чтобы обратиться к 0 элементу соответствующего языкового массива (с записанным нзванием языка)for a in ${lng_sfx[@]} ; dolocal d="language_$a[@]"; d=("${!d}")# Продолжаем формирование массива для вывода языкового меню# В каждый элемент в текстовую часть добавляем название языка, а через# разделитель [;], в командную часть, добавляем команду вызова функции# [languages] и в качестве аргумента ставим код языка. Для английского языка# получится ["English;languages set en"], где [set en] - аргументы для функции [languages]menu2+=("$d;languages set $a")done# Добавляем в меню команду выходаmenu2+=("${lng[1]};exit")# Передаем сформированный массив с языковым меню в функцию вывода на экран# Вторым аргументом идет сообщение, которое отобразится в строке приглашенияprints "menu2[@]" "${msg[6]}"}options() {# Функция создания меню с настройками# В каждый элемент в текстовую часть добавляем название необходимого пункта меню,# а через разделитель [;], в командную часть, добавляем соответствующую команду вызова функцииlocal menu1=("${lng[2]};options" "${lng[7]} [$langset];langmenu" "${lng[1]};exit")# Передаем массив в функцию вывода на экран# Вторым аргументом идет сообщение, которое отобразится в строке приглашенияprints "menu1[@]" "${msg[1]}"}main() {# Функция создания основного меню# В каждый элемент в текстовую часть добавляем название необходимого пункта меню,# а через разделитель [;], в командную часть, добавляем соответствующую команду вызова функции# Здесь и в других массивах, содержащих меню, первая запись - это название и команда вызова текущей функции# Необходимо для того, чтобы передавать название меню для печати шапки и для вызова этой функции при необходимостиlocal menu0=("${lng[3]};main" "${lng[4]};gitadd" "${lng[5]};gitinit" "${lng[2]};options" "${lng[1]};exit")while true ; do# Передаем массив в функцию вывода на экран# Вторым аргументом идет сообщение, которое отобразится в строке приглашенияprints "menu0[@]" "${msg[1]}"done}mainexit 0
Подробнее..

Красные глаза

29.07.2020 22:04:59 | Автор: admin

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

Все кто работал в консоли какого-нибудь Линукса наверно отмечали удобную особенность отображать текущую папку, имя пользователя, имя сервера и что-то еще в зависимости от дистра в строке приглашения. Мне тоже это всегда нравилось. Но иногда получалось вот так:


Сложно творить в строке из трех символов, конечно курсор переходит на следующую строку но это скорей еще больше бесит чем помогает. И вот в какой-то момент я сказал себе:


Я решил вернуть себе все пространство командной строки и больше не отдавать никому и никогда. Встал вопрос, как изменить текст приглашения командной строки? Оказалось, очень просто, достаточно изменить специальную системную переменную `PS1`.


Да, прямо в терминале можно задать новый текст приглашения. Но как сохранить изменения? Да и без информации о текущем каталоге как-то неуютно себя чувствуешь, постоянно задаешь себе вопрос: Где я? Поможет файл ~/.bashrc, в нём можно сохранить изменение PS1, а чтобы информация о текущей директории не занимала рабочее пространство, я решил разместить её не В а НАД командной строкой. Добавим в файл ~/.bashrc строку:
PS1='$PWD\n# '

Обратите внимания на одинарные кавычки, если мы используем двойные, то вместо указателя на переменную $PWD (системная переменная, в которой храниться полный путь текущей папки, аналог команда pwd) в строку приглашения запишется ее значение(текущий каталог) и меняться при переходе из папки в папку не будет. Выглядит это так:


Командная строка полностью свободна, но имя папки сливается с содержимым если выполнить команду ls. Придется отделить мух от котлет имя папки от содержимого. Я решил добавить рамки для $PWD, добавив сверху и снизу строки из символов "-". Как узнать количество символов в строке? Для этого тоже есть системная переменная $COLUMNS. А чтобы быстро сформировать строку с необходимым количеством символов "-" воспользуемся командой printf:
printf -v line "%${COLUMNS}s"

Эта команда создаст переменную $line и заполнит её пробелами в количестве $COLUMNS но нам нужны не пробелы а "-", для этого используем следующий трюк:
line=${line// /-} # заменить все пробелы на -

Добавим этот код в ~/.bashrc
printf -v line "%${COLUMNS}s"line=${line// /-}PS1='\n$line\n$PWD\n$line\n# '



Отлично, но если сейчас изменить размер окна терминала, размер линий не изменится и красота пропадет:


Чтобы поправить ситуацию перенесем новый код в функцию info и добавим её в PS1:
info () {    printf -v line "%${COLUMNS}s"    line=${line// /-}    printf "\n$line\n$PWD\n$line\n# "}PS1='$(info)'

Можно совместить приятное с полезным, добавив в верхний ограничитель вставку с именем хоста. Вставка с именем будет посередине, для этого нам необходимо вычислить центр линии(с учетом длинны названия хоста и доп. символов):
info () {    name_lenght="{ $HOSTNAME }"    name_lenght=${#name_lenght}    top_line_left=$[(COLUMNS-name_lenght)/2]    top_line_right=$[COLUMNS-(top_line_left+name_lenght)]    printf -v top_line "%${top_line_left}s{_S_${HOSTNAME}_S_}%${top_line_right}s"    printf -v bot_line "%${COLUMNS}s"    bot_line=${bot_line// /-}    top_line=${top_line// /-}    top_line=${top_line//_S_/ }    printf "\n$top_line\n$PWD\n$bot_line\n# "}PS1='$(info)'



Я заключил имя хоста в фигурные скобки с пробелами, но вместо пробелов вокруг $HOSTNAME используются символы "_S_" которые затем меняются на пробелы. Это необходимо потому, что в итоговой строке все пробелы заменяются на "-", а внутри вставки должны остаться пробелы. Добавим красок, для этого подготовим переменные с кодами изменения цвета текста в терминале, я использовал такие цвета:
RED='\e[31m' # красныйGRN='\e[32m' # зелёныйYLW='\e[33m' # жёлтыйBLU='\e[34m' # синийMGN='\e[35m' # пурпурныйDEF='\e[0m'  # вернуть значения по умолчаниюBLD='\e[1m'  # жирноDIM='\e[2m'  # тускло

Добавим эти переменные в наш код:
info () {    name_lenght="{ $HOSTNAME }"    name_lenght=${#name_lenght}    top_line_left=$[(COLUMNS-name_lenght)/2]    top_line_right=$[COLUMNS-(top_line_left+name_lenght)]    printf -v top_line "%${top_line_left}s{_S_$DEF$BLD$HOSTNAME${DEF}_S_$GRN}%${top_line_right}s"    printf -v bot_line "%${COLUMNS}s"    bot_line=$GRN${bot_line// /-}$DEF    top_line=${top_line// /-}    top_line=$GRN${top_line//_S_/ }$DEF    printf "\n$top_line\n$BLD$BLU$PWD$DEF\n$bot_line\n# "}PS1='$(info)'



Идем дальше, правая часть выглядит пусто, можно разместить там дату, и время:
printf -v date "%(%a %d %b %T)T"

Чтобы разместить это справа необходимо добавить какое-то количество пробелов после $PWD, какое, давайте посчитаем:
center_space=$[COLUMNS-${#date}-${#PWD}]((center_space<0)) && center_space=1...printf "\n$top_line\n$BLD$BLU$PWD$DEF%${center_space}s$DIM$date\n$bot_line\n# "



Можно ли сделать лучше? Конечно, добавим вывод git status если находимся в папке с git проектом:
    git_tst= git_clr=    [[ -d .git ]] && {        git_tst=(GIT $(git status -sb))        git_tst="${git_tst[*]} " # простой для теста        git_clr=(GIT $(git -c color.ui=always status -sb))        git_clr="${git_clr[*]} " # цветной для вывода    }    ...    center_space=$[COLUMNS-${#date}-${#PWD}-${#git_tst}]    ...    printf "\n$top_line\n$BLD$BLU$PWD$DEF%${center_space}s${git_clr[*]}$DIM$date\n$bot_line\n\$ "



Обратите внимания что git_clr и git_tst сначала записываются массивом, а затем преобразуются в переменные. Это необходимо для того чтобы удалить переходы строки из вывода git status. Но где же глаза? Сейчас будут глаза О_о, создадим массив с базовым набором глаз:
eyes=(O o    )

И посчитаем их количество:
en=${#eyes[@]}

Добавим символ рта:
mouth='_'

И сделаем генератор случайных морд:
face () {    printf "$YLW${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}$DEF"}

Глаза будут находиться по краям информационного поля, необходимо учесть их при расчете количества пробелов в середине, подготовим для этого отдельную переменную:
face_tst='O_o  o_O'...center_space=$[COLUMNS-${#date}-${#PWD}-${#git_tst}-${#face_tst}]printf "\n$top_line\n$(face) $BLD$BLU$PWD$DEF%${center_space}s$git_clr$DIM$date $(face)\n$bot_line\n\$ "



Как заставить глаза покраснеть? Долго сидеть за компом, не спать На stackoverflow.com мне попался интересный вопрос, автор спрашивает: как менять цвет в приглашении командной строки на красный если последняя команда завершилась неудачно? Это и натолкнуло меня на идею красных глаз. Добавим в функцию info запоминание статуса завершения последней команды:
info () {    error=$?    ...}

И изменим функцию face таким образом, чтобы она проверяла переменную $error и в зависимости от её значения красила глаза в красный или жёлтый:
face () {    [[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW    printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"}



Ну вот у меня глаза покраснели, но можно добавить еще кое-что. Добавим проверку переменной $debian_chroot:
[[ $debian_chroot ]] && chrt="($debian_chroot)" || chrt=...name_lenght="{ $HOSTNAME$chrt }"...printf -v top_line "%${top_line_left}s{_S_$DEF$BLD$HOSTNAME$chrt${DEF}_S_$GRN}%${top_line_right}s"

И изменим текст в заголовке окна терминала:
PS1='$(info)'; case "$TERM" in xterm*|rxvt*) PS1="\[\e]0;$(face 1) \w\a\]$PS1";; esac

В заголовок будет выводится морда и текущий каталог, но придется немного доработать функцию face чтобы она рисовала морду без цветовых кодов, они в заголовке бутут отображаться просто текстом, передадим функции face какой-нибудь параметр (например 1), внутри функции добавим проверку, если задан 1-й аргумент, выдать морду без разукрашивания:
face () {    [[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW    [[ $1 ]] && printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}" \             || printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"}



Итоговый скрипт:
RED='\e[31m' # красныйGRN='\e[32m' # зелёныйYLW='\e[33m' # жёлтыйBLU='\e[34m' # синийMGN='\e[35m' # пурпурныйDEF='\e[0m'  # вернуть значения по умолчаниюBLD='\e[1m'  # жирноDIM='\e[2m'  # тусклоeyes=(O o    ) en=${#eyes[@]} mouth='_'face () {    [[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW    [[ $1 ]] && printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}" \             || printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"}info () { error=$? git_tst= git_clr=    [[ -d .git ]] && {        git_tst=(GIT $(git status -sb))        git_tst="${git_tst[*]} " # простой для теста        git_clr=(GIT $(git -c color.ui=always status -sb))        git_clr="${git_clr[*]} " # цветной для вывода    }    [[ $debian_chroot ]] && chrt="($debian_chroot)" || chrt=    name_lenght="{ $HOSTNAME$chrt }"    name_lenght=${#name_lenght}    face_tst='O_o  o_O'    top_line_left=$[(COLUMNS-name_lenght)/2]    top_line_right=$[COLUMNS-(top_line_left+name_lenght)]    printf -v top_line "%${top_line_left}s{_S_$DEF$BLD$HOSTNAME$chrt${DEF}_S_$GRN}%${top_line_right}s"    printf -v bot_line "%${COLUMNS}s"    printf -v date  "%(%a %d %b %T)T"    top_line=${top_line// /-}    top_line=$GRN${top_line//_S_/ }$DEF    bot_line=$GRN${bot_line// /-}$DEF    center_space=$[COLUMNS-${#date}-${#PWD}-${#git_tst}-${#face_tst}]    ((center_space<0)) && center_space=1    printf "\n$top_line\n$(face) $BLD$BLU$PWD$DEF%${center_space}s$git_clr$DIM$date $(face)\n$bot_line\n\$ "}PS1='$(info)'; case "$TERM" in xterm*|rxvt*) PS1="\[\e]0;$(face 1) \w\a\]$PS1";; esac

На этом все, спасибо за внимание!) Подписывайтесь, ставьте лайки, вот это вот все, проект есть в гитхабе info-bar Творите, выдумывайте, пробуйте!)
проверка внимательности
Вы заметили в какой момент символ "#" в строке приглашения поменялся на "$"?)
Подробнее..
Категории: Cli , Bash , Tutorial , Кодобред , Redeyes

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru