Загрузка данных


1. lib/common-colors.sh

Это базовый модуль. Его нужно подключать первым.

#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Общие цвета и функции вывода
#
# Используется другими модулями:
#   - check-root.sh
#   - ensure-executable.sh
#   - set-locale.sh
#   - ask-yes-no.sh
#   - ask-reboot.sh
#
# Подключение:
#
#   source "${SCRIPT_DIR}/lib/common-colors.sh"
# ═══════════════════════════════════════════════════════════════




# ═══════════════════════════════════════════════════════════════
# Основные цвета
# ═══════════════════════════════════════════════════════════════


RD=$(echo "\033[01;31m")
YW=$(echo "\033[33m")
GN=$(echo "\033[1;92m")
BL=$(echo "\033[1;34m")
CL=$(echo "\033[m")




# ═══════════════════════════════════════════════════════════════
# Дополнительные цвета
# ═══════════════════════════════════════════════════════════════


GREEN="\e[32m"
RED="\e[31m"
YELLOW="\e[33m"
BLUE="\e[34m"
NC="\e[0m"




# ═══════════════════════════════════════════════════════════════
# Символы
# ═══════════════════════════════════════════════════════════════


BFR="\\r\\033[K"
HOLD="-"


CM="${GN}✓${CL}"
CROSS="${RD}✗${CL}"
ARROW="${BL}➜${CL}"




# ═══════════════════════════════════════════════════════════════
# Информационное сообщение
# ═══════════════════════════════════════════════════════════════


msg_info() {
    local msg="$1"


    echo -ne " ${HOLD} ${YW}${msg}..."
}




# ═══════════════════════════════════════════════════════════════
# Успешное выполнение
# ═══════════════════════════════════════════════════════════════


msg_ok() {
    local msg="$1"


    echo -e "${BFR} ${CM} ${GN}${msg}${CL}"
}




# ═══════════════════════════════════════════════════════════════
# Ошибка
# ═══════════════════════════════════════════════════════════════


msg_error() {
2. lib/check-root.sh
#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Проверка прав root
#
# Функция:
#
#   check_root
#
# Проверяет, запущен ли основной скрипт
# с правами суперпользователя root.
#
# Требует:
#
#   common-colors.sh
#
# потому что используется msg_error().
# ═══════════════════════════════════════════════════════════════




check_root() {


    # EUID — эффективный UID текущего пользователя.
    #
    # UID 0 = root.
    #
    # Если EUID не равен 0,
    # скрипт запущен не от root.
    if [[ $EUID -ne 0 ]]; then


        # Выводим сообщение об ошибке.
        msg_error "Скрипт требует прав root!"


        # Показываем пользователю,
        # как перейти в root.
        echo -e " ${YW}Запустите от root:${CL} su -"


        # Завершаем выполнение программы.
        exit 1
    fi
}
3. lib/ensure-executable.sh
#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Проверка права на выполнение
#
# Функция:
#
#   ensure_executable "/путь/к/файлу.sh"
#
# Проверяет:
#
#   1. Передан ли путь к файлу.
#   2. Существует ли файл.
#   3. Имеет ли файл право на выполнение (+x).
#
# Если +x отсутствует — автоматически выполняется:
#
#   chmod +x
#
# Требует:
#
#   common-colors.sh
#
# для msg_info() и msg_ok().
# ═══════════════════════════════════════════════════════════════




ensure_executable() {


    # Получаем путь к файлу из первого аргумента.
    local target="${1:-}"




    # ═══════════════════════════════════════════════════════════
    # Проверка аргумента
    # ═══════════════════════════════════════════════════════════


    if [ -z "$target" ]; then


        echo "ensure_executable: не указан файл" >&2


        return 1
    fi




    # ═══════════════════════════════════════════════════════════
    # Проверка существования файла
    # ═══════════════════════════════════════════════════════════


    if [ ! -e "$target" ]; then


        echo "ensure_executable: файл не найден: $target" >&2


        return 1
    fi




    # ═══════════════════════════════════════════════════════════
    # Проверка права +x
    # ═══════════════════════════════════════════════════════════


    # Если файл уже исполняемый,
    # ничего менять не требуется.
    if [ -x "$target" ]; then


        return 0
    fi




    # ═══════════════════════════════════════════════════════════
    # Проверка root
    # ═══════════════════════════════════════════════════════════


    # Для изменения прав файла
    # необходимы соответствующие права.
    if [ "$(id -u)" -ne 0 ]; then


        echo "ensure_executable: нужны права root (chmod +x)" >&2


        echo "  Запустите: su -   или   sudo $0 $target" >&2


        return 1
    fi




    # ═══════════════════════════════════════════════════════════
    # Добавление права +x
    # ═══════════════════════════════════════════════════════════


    if declare -F msg_info >/dev/null 2>&1; then


        msg_info "Файл не исполняемый, добавляем права (+x)"
4. lib/ask-yes-no.sh

Это универсальный модуль для вопросов y/n.

#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Запрос да/нет
#
# Использование:
#
#   ask_yes_no "Продолжить? (y/n): "
#
# Возвращает:
#
#   0 — пользователь ответил Y/y
#   1 — любой другой ответ
#
#
# Также доступна функция:
#
#   ask_confirm_or_exit
#
# При отказе завершает основной скрипт.
#
# Требует:
#
#   common-colors.sh
# ═══════════════════════════════════════════════════════════════




# ═══════════════════════════════════════════════════════════════
# Проверяем наличие msg_ok
# ═══════════════════════════════════════════════════════════════


if ! declare -F msg_ok >/dev/null 2>&1; then


    _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"


    # shellcheck disable=SC1091
    source "${_LIB_DIR}/common-colors.sh"


    unset _LIB_DIR
fi




# ═══════════════════════════════════════════════════════════════
# Запрос Y/N
# ═══════════════════════════════════════════════════════════════


ask_yes_no() {


    # Текст вопроса является обязательным аргументом.
    local prompt="${1:?ask_yes_no: нужен текст вопроса}"


    local answer


    # Читаем ответ пользователя.
    read -r -p "$prompt" answer


    # Проверяем ответ.
    case "$answer" in


        # Y или y — положительный ответ.
        [Yy]*)
            return 0
            ;;


        # Любой другой ответ — отрицательный.
        *)
            return 1
            ;;


    esac
}




# ═══════════════════════════════════════════════════════════════
# Подтверждение или выход
# ═══════════════════════════════════════════════════════════════


ask_confirm_or_exit() {


    # Вопрос по умолчанию.
    local prompt="${1:-Продолжить установку? (y/n): }"


    # Если пользователь согласился —
    # продолжаем выполнение.
    if ask_yes_no "$prompt"; then


        return 0
    fi




    # Если пользователь отказался —
    # сообщаем об отмене.
    msg_ok "Отменено пользователем"


    # Завершаем основной скрипт.
    exit 0
}
5. lib/ask-reboot.sh

Теперь перезагрузка тоже отдельным модулем.

#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Запрос на перезагрузку
#
# Использование:
#
#   ask_reboot
#
# Перезагрузка через 5 секунд.
#
#
# Можно указать собственную задержку:
#
#   ask_reboot 10
#
# В этом случае перезагрузка произойдёт
# через 10 секунд.
#
# Требует:
#
#   common-colors.sh
#   ask-yes-no.sh
# ═══════════════════════════════════════════════════════════════




# ═══════════════════════════════════════════════════════════════
# Подключение common-colors.sh
# ═══════════════════════════════════════════════════════════════


if ! declare -F msg_info >/dev/null 2>&1; then


    _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"


    # shellcheck disable=SC1091
    source "${_LIB_DIR}/common-colors.sh"


    unset _LIB_DIR
fi




# ═══════════════════════════════════════════════════════════════
# Подключение ask-yes-no.sh
# ═══════════════════════════════════════════════════════════════


if ! declare -F ask_yes_no >/dev/null 2>&1; then


    _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"


    # shellcheck disable=SC1091
    source "${_LIB_DIR}/ask-yes-no.sh"


    unset _LIB_DIR
fi




# ═══════════════════════════════════════════════════════════════
# Запрос на перезагрузку
# ═══════════════════════════════════════════════════════════════


ask_reboot() {


    # Первый аргумент — задержка перед перезагрузкой.
    #
    # Если аргумент не передан,
    # используется 5 секунд.
    local delay="${1:-5}"




    # Спрашиваем пользователя.
    if ask_yes_no "Перезагрузить систему сейчас? (y/n): "; then


        # Показываем обратный отсчёт.
        msg_info "Перезагрузка через ${delay} секунд"


        # Небольшая задержка.
        sleep "$delay"


        # Перезагружаем систему.
        reboot


    else


        # Если пользователь отказался.
        msg_ok "Не забудьте перезагрузить систему позже!"


    fi
}
6. lib/set-locale.sh

Здесь находится только логика русификации.

#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Русификация Kali Linux
#
# Функция:
#
#   set_locale
#
# Выполняет:
#
#   1. Проверку пакета locales.
#   2. Установку locales при необходимости.
#   3. Включение ru_RU.UTF-8.
#   4. Генерацию локали.
#   5. Установку ru_RU.UTF-8 по умолчанию.
#
# Перезагрузка здесь НЕ выполняется.
#
# За перезагрузку отвечает:
#
#   ask-reboot.sh
# ═══════════════════════════════════════════════════════════════




set_locale() {


    # ═══════════════════════════════════════════════════════════
    # Проверка пакета locales
    # ═══════════════════════════════════════════════════════════


    msg_info "Проверяем пакет locales"


    if ! dpkg -s locales >/dev/null 2>&1; then


        msg_info "Пакет locales не установлен"


        # Обновляем список пакетов.
        apt update


        # Устанавливаем locales.
        apt install -y locales


        msg_ok "Пакет locales установлен"


    else


        msg_ok "Пакет locales уже установлен"


    fi




    # ═══════════════════════════════════════════════════════════
    # Настройка ru_RU.UTF-8
    # ═══════════════════════════════════════════════════════════


    msg_info "Настраиваем ru_RU.UTF-8"




    # Проверяем, закомментирована ли русская локаль.
    if grep -q '^# *ru_RU.UTF-8 UTF-8' /etc/locale.gen; then


        # Убираем # перед ru_RU.UTF-8.
        sed -i \
            's/^# *ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/' \
            /etc/locale.gen




    # Если строки нет вообще —
    # добавляем её.
    elif ! grep -q '^ru_RU.UTF-8 UTF-8' /etc/locale.gen; then


        echo 'ru_RU.UTF-8 UTF-8' >> /etc/locale.gen


    fi




    # ═══════════════════════════════════════════════════════════
    # Генерация локали
    # ═══════════════════════════════════════════════════════════


    msg_info "Генерируем ru_RU.UTF-8"


7. russification.sh

И наконец, главный файл. Именно его запускаешь ты.

#!/usr/bin/env bash


# ═══════════════════════════════════════════════════════════════
# Kali Linux — Русификация
#
# Главный скрипт.
#
# Запуск:
#
#   ./russification.sh
#
# Или:
#
#   bash russification.sh
#
# Для работы требуются права root.
# ═══════════════════════════════════════════════════════════════




# ═══════════════════════════════════════════════════════════════
# Определяем директорию проекта
# ═══════════════════════════════════════════════════════════════


SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"




# ═══════════════════════════════════════════════════════════════
# Подключение библиотек
# ═══════════════════════════════════════════════════════════════
#
# Порядок важен:
#
# 1. common-colors.sh
# 2. check-root.sh
# 3. ensure-executable.sh
# 4. ask-yes-no.sh
# 5. ask-reboot.sh
# 6. set-locale.sh
#
# ═══════════════════════════════════════════════════════════════




# Общие цвета и функции вывода.
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/common-colors.sh"




# Проверка root.
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/check-root.sh"




# Проверка прав +x.
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/ensure-executable.sh"




# Запросы Y/N.
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/ask-yes-no.sh"




# Запрос перезагрузки.
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/ask-reboot.sh"




# Русификация.
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/set-locale.sh"




# ═══════════════════════════════════════════════════════════════
# Проверка root
# ═══════════════════════════════════════════════════════════════


check_root




# ═══════════════════════════════════════════════════════════════
# Проверка прав на выполнение
# ═══════════════════════════════════════════════════════════════


ensure_executable "${SCRIPT_DIR}/russification.sh"


ensure_executable "${SCRIPT_DIR}/lib/common-colors.sh"
# ═══════════════════════════════════════════════════════════════
ask_reboot 5
В итоге

Структура:

kali-russification/
│
├── russification.sh              ← ЗАПУСКАЕШЬ ЭТОТ
│
└── lib/
    │
    ├── common-colors.sh          ← цвета / сообщения
    │
    ├── check-root.sh             ← проверка root
    │
    ├── ensure-executable.sh      ← проверка +x
    │
    ├── ask-yes-no.sh             ← вопросы y/n
    │
    ├── ask-reboot.sh             ← запрос перезагрузки
    │
    └── set-locale.sh             ← русификация