Created
April 7, 2026 07:16
-
-
Save hightemp/dd129bcdfec012744e5cfa2f380d197d to your computer and use it in GitHub Desktop.
Скрипт-помощник для обновления ubuntu с 20 до 24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Ubuntu 20.04 → 22.04 → 24.04 upgrade script | |
| # Идемпотентный: запускай повторно после каждой перезагрузки | |
| set -euo pipefail | |
| # ─── Константы ─────────────────────────────────────────────────────────────── | |
| STATE_DIR="/var/lib/ubuntu-upgrade" | |
| STATE_FILE="$STATE_DIR/state" | |
| PPA_BACKUP_DIR="$STATE_DIR/ppa_backup" | |
| LOG_DIR="/var/log/ubuntu-upgrade" | |
| LOG_FILE="$LOG_DIR/upgrade.log" | |
| SCRIPT_PATH="$(realpath "$0")" | |
| # ─── Цвета ─────────────────────────────────────────────────────────────────── | |
| RED='\033[0;31m' | |
| GRN='\033[0;32m' | |
| YLW='\033[1;33m' | |
| BLU='\033[0;34m' | |
| CYN='\033[0;36m' | |
| BLD='\033[1m' | |
| RST='\033[0m' | |
| # ─── Утилиты вывода ────────────────────────────────────────────────────────── | |
| info() { echo -e "${BLU}[INFO]${RST} $*" | tee -a "$LOG_FILE"; } | |
| ok() { echo -e "${GRN}[OK]${RST} $*" | tee -a "$LOG_FILE"; } | |
| warn() { echo -e "${YLW}[WARN]${RST} $*" | tee -a "$LOG_FILE"; } | |
| error() { echo -e "${RED}[ERROR]${RST} $*" | tee -a "$LOG_FILE"; } | |
| step() { | |
| echo -e "\n${BLD}${CYN}══════════════════════════════════════${RST}" | tee -a "$LOG_FILE" | |
| echo -e "${BLD}${CYN} $*${RST}" | tee -a "$LOG_FILE" | |
| echo -e "${BLD}${CYN}══════════════════════════════════════${RST}" | tee -a "$LOG_FILE" | |
| } | |
| die() { error "$*"; error "Лог: $LOG_FILE"; exit 1; } | |
| # ─── Инициализация ─────────────────────────────────────────────────────────── | |
| init_dirs() { | |
| mkdir -p "$STATE_DIR" "$PPA_BACKUP_DIR" "$LOG_DIR" | |
| touch "$LOG_FILE" | |
| { | |
| echo "──────────────────────────────────────────" | |
| echo "Запуск: $(date '+%Y-%m-%d %H:%M:%S')" | |
| echo "Ubuntu: $(lsb_release -ds 2>/dev/null || cat /etc/os-release | grep PRETTY | cut -d= -f2)" | |
| echo "──────────────────────────────────────────" | |
| } >> "$LOG_FILE" | |
| } | |
| # ─── Состояние ─────────────────────────────────────────────────────────────── | |
| get_state() { | |
| [[ -f "$STATE_FILE" ]] && cat "$STATE_FILE" || echo "start" | |
| } | |
| set_state() { | |
| echo "$1" > "$STATE_FILE" | |
| info "Состояние сохранено: $1" | |
| } | |
| # ─── Версия Ubuntu ─────────────────────────────────────────────────────────── | |
| get_ubuntu_version() { | |
| # shellcheck disable=SC1091 | |
| . /etc/os-release | |
| echo "$VERSION_ID" | |
| } | |
| # ─── Проверка прав ─────────────────────────────────────────────────────────── | |
| check_root() { | |
| if [[ $EUID -ne 0 ]]; then | |
| echo -e "${RED}Запусти скрипт с sudo:${RST} sudo bash $SCRIPT_PATH" | |
| exit 1 | |
| fi | |
| } | |
| # ─── Предполётные проверки ─────────────────────────────────────────────────── | |
| preflight_checks() { | |
| local target_version="$1" | |
| step "Предполётные проверки перед апгрейдом до Ubuntu $target_version" | |
| local errors=0 | |
| info "Текущая версия: Ubuntu $(get_ubuntu_version)" | |
| # Питание (ноутбук) | |
| local battery_path="" | |
| for p in /sys/class/power_supply/BAT*; do | |
| [[ -e "$p" ]] && battery_path="$p" && break | |
| done | |
| if [[ -n "$battery_path" ]]; then | |
| local status capacity | |
| status=$(cat "$battery_path/status" 2>/dev/null || echo "Unknown") | |
| capacity=$(cat "$battery_path/capacity" 2>/dev/null || echo "0") | |
| if [[ "$status" != "Charging" && "$status" != "Full" ]]; then | |
| if (( capacity < 50 )); then | |
| error "🔋 Заряд батареи: ${capacity}% — недостаточно для апгрейда!" | |
| error " Подключи зарядное устройство и запусти скрипт снова." | |
| (( errors++ )) || true | |
| else | |
| warn "🔋 Батарея не заряжается (${capacity}%). Рекомендуется подключить зарядку." | |
| fi | |
| else | |
| ok "🔋 Питание: $status (${capacity}%)" | |
| fi | |
| else | |
| info "🔋 Батарея не обнаружена (возможно, стационарный ПК или ноутбук с полным зарядом)" | |
| fi | |
| # Место на диске / | |
| local root_free_mb | |
| root_free_mb=$(df -BM / | awk 'NR==2{gsub(/M/,"",$4); print $4}') | |
| if (( root_free_mb < 5120 )); then | |
| error "💾 Мало места на /: ${root_free_mb} МБ (нужно минимум 5 ГБ)" | |
| error " Освободи место:" | |
| error " sudo apt autoremove --purge" | |
| error " sudo apt clean" | |
| error " sudo journalctl --vacuum-size=200M" | |
| (( errors++ )) || true | |
| else | |
| ok "💾 Место на /: ${root_free_mb} МБ — достаточно" | |
| fi | |
| # Место на /boot | |
| local boot_free_mb=999 | |
| if mountpoint -q /boot 2>/dev/null; then | |
| boot_free_mb=$(df -BM /boot | awk 'NR==2{gsub(/M/,"",$4); print $4}') | |
| fi | |
| if (( boot_free_mb < 200 )); then | |
| error "💾 Мало места в /boot: ${boot_free_mb} МБ (нужно >200 МБ)" | |
| error " Удали старые ядра:" | |
| error " dpkg -l | grep 'linux-image' # посмотреть ядра" | |
| error " sudo apt autoremove --purge # автоудаление" | |
| (( errors++ )) || true | |
| else | |
| ok "💾 Место в /boot: ${boot_free_mb} МБ" | |
| fi | |
| # dpkg — нет незавершённых операций | |
| local dpkg_audit | |
| dpkg_audit=$(dpkg --audit 2>&1 || true) | |
| if [[ -n "$dpkg_audit" ]]; then | |
| error "⚙️ Есть проблемные пакеты в dpkg:" | |
| echo "$dpkg_audit" | tee -a "$LOG_FILE" | |
| error " Исправь:" | |
| error " sudo dpkg --configure -a" | |
| error " sudo apt --fix-broken install" | |
| (( errors++ )) || true | |
| else | |
| ok "⚙️ dpkg: нет незавершённых операций" | |
| fi | |
| # Удержанные пакеты | |
| local held | |
| held=$(apt-mark showhold 2>/dev/null || true) | |
| if [[ -n "$held" ]]; then | |
| warn "📌 Удержанные пакеты (held): $held" | |
| warn " Рекомендуется снять: sudo apt-mark unhold <пакет>" | |
| else | |
| ok "📌 Удержанных пакетов нет" | |
| fi | |
| # Сторонние PPA | |
| local ppa_count | |
| ppa_count=$(find /etc/apt/sources.list.d/ -name "*.list" 2>/dev/null | wc -l) | |
| if (( ppa_count > 0 )); then | |
| warn "📋 Сторонних репозиториев: $ppa_count — будут отключены перед апгрейдом" | |
| else | |
| ok "📋 Сторонних репозиториев нет" | |
| fi | |
| # SSH-предупреждение | |
| if [[ -n "${SSH_CONNECTION:-}" ]]; then | |
| warn "🌐 Запущено по SSH — убедись, что есть запасной доступ!" | |
| warn " Рекомендуется: tmux new -s upgrade" | |
| fi | |
| echo "" | tee -a "$LOG_FILE" | |
| if (( errors > 0 )); then | |
| die "Найдено критических проблем: $errors. Исправь их и запусти скрипт снова." | |
| fi | |
| ok "Все предполётные проверки пройдены ✓" | |
| } | |
| # ─── PPA management ────────────────────────────────────────────────────────── | |
| disable_ppas() { | |
| step "Отключение сторонних репозиториев" | |
| local count=0 | |
| while IFS= read -r -d '' f; do | |
| cp "$f" "$PPA_BACKUP_DIR/$(basename "$f")" | |
| mv "$f" "${f}.disabled" | |
| info "Отключён: $(basename "$f")" | |
| (( count++ )) || true | |
| done < <(find /etc/apt/sources.list.d/ -name "*.list" -print0 2>/dev/null) | |
| if (( count > 0 )); then | |
| ok "Отключено PPA: $count (бэкап: $PPA_BACKUP_DIR)" | |
| else | |
| ok "Сторонних репозиториев не было" | |
| fi | |
| apt-get update -qq 2>&1 | tee -a "$LOG_FILE" || true | |
| } | |
| restore_ppas() { | |
| step "Восстановление сторонних репозиториев" | |
| local restored=0 | |
| while IFS= read -r -d '' f; do | |
| mv "$f" "${f%.disabled}" | |
| info "Восстановлен: $(basename "${f%.disabled}")" | |
| (( restored++ )) || true | |
| done < <(find /etc/apt/sources.list.d/ -name "*.disabled" -print0 2>/dev/null) | |
| if (( restored > 0 )); then | |
| warn "Восстановлено PPA: $restored" | |
| warn "Проверь совместимость каждого PPA с новой версией Ubuntu!" | |
| warn "Если после apt update есть ошибки — отключи проблемный PPA:" | |
| warn " sudo add-apt-repository --remove ppa:user/repo" | |
| apt-get update 2>&1 | tee -a "$LOG_FILE" || \ | |
| warn "apt update выдал предупреждения после восстановления PPA — проверь вручную" | |
| else | |
| ok "PPA для восстановления не найдены" | |
| fi | |
| } | |
| # ─── Timeshift снапшот ─────────────────────────────────────────────────────── | |
| take_snapshot() { | |
| local label="$1" | |
| if command -v timeshift &>/dev/null; then | |
| step "Создание снапшота Timeshift: $label" | |
| if timeshift --create --comments "$label" --yes 2>&1 | tee -a "$LOG_FILE"; then | |
| ok "Снапшот создан: $label" | |
| else | |
| warn "Timeshift не смог создать снапшот — возможно, не настроен." | |
| warn "Продолжаем без снапшота. Рекомендуется вручную сохранить важные данные." | |
| fi | |
| else | |
| warn "Timeshift не установлен. Снапшот не создан." | |
| warn "Установи: sudo apt install timeshift — затем настрой и создай снапшот вручную" | |
| fi | |
| } | |
| # ─── Объяснение ошибок apt ─────────────────────────────────────────────────── | |
| explain_apt_error() { | |
| local rc="$1" | |
| local log_tail | |
| log_tail=$(tail -40 "$LOG_FILE" 2>/dev/null || true) | |
| error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| error " APT/dpkg завершился с кодом ошибки: $rc" | |
| error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| if echo "$log_tail" | grep -qE "lock-frontend|Could not open lock|unable to acquire"; then | |
| error "" | |
| error "🔒 ПРИЧИНА: Файл блокировки dpkg/apt занят другим процессом" | |
| error "" | |
| error " Решение:" | |
| error " 1. Подожди 1-2 мин — возможно, идёт фоновое auto-update" | |
| error " 2. Найди блокирующий процесс:" | |
| error " sudo lsof /var/lib/dpkg/lock-frontend" | |
| error " 3. Убей если завис: sudo kill -9 <PID>" | |
| error " 4. Если процесса нет — удали lock-файлы:" | |
| error " sudo rm -f /var/lib/apt/lists/lock" | |
| error " sudo rm -f /var/cache/apt/archives/lock" | |
| error " sudo rm -f /var/lib/dpkg/lock /var/lib/dpkg/lock-frontend" | |
| error " sudo dpkg --configure -a" | |
| error " 5. Запусти скрипт снова" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -qE "broken packages|fix-broken|unmet dep|dependency prob"; then | |
| error "" | |
| error "💥 ПРИЧИНА: Сломанные или неудовлетворённые зависимости" | |
| error "" | |
| error " Решение:" | |
| error " sudo dpkg --configure -a" | |
| error " sudo apt --fix-broken install" | |
| error " sudo apt update && sudo apt dist-upgrade" | |
| error " Затем запусти скрипт снова" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -q "kept back"; then | |
| error "" | |
| error "📌 ПРИЧИНА: Пакеты удерживаются (held back) и не обновляются" | |
| error "" | |
| error " Решение:" | |
| error " sudo apt full-upgrade" | |
| error " # или:" | |
| error " sudo apt-mark showhold" | |
| error " sudo apt-mark unhold <пакет>" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -qE "No space left|ENOSPC"; then | |
| error "" | |
| error "💾 ПРИЧИНА: Закончилось место на диске" | |
| error "" | |
| error " Решение:" | |
| error " df -h # где мало места" | |
| error " sudo apt autoremove --purge # удалить ненужные пакеты" | |
| error " sudo apt clean # очистить кэш apt" | |
| error " sudo journalctl --vacuum-size=200M # очистить systemd-логи" | |
| error " sudo find /var/log -name '*.gz' -delete # старые логи" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -qE "^Err:|404 Not Found|Failed to fetch|Unable to fetch"; then | |
| error "" | |
| error "🌐 ПРИЧИНА: Репозиторий недоступен или URL устарел (возможно — старый PPA)" | |
| error "" | |
| error " Решение:" | |
| error " sudo apt update 2>&1 | grep '^Err:'" | |
| error " ls /etc/apt/sources.list.d/ # найди проблемный файл" | |
| error " sudo mv /etc/apt/sources.list.d/<проблема>.list /tmp/" | |
| error " sudo apt update" | |
| error " Затем запусти скрипт снова" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -qE "dpkg.*error|subprocess.*returned error|post-installation"; then | |
| error "" | |
| error "⚙️ ПРИЧИНА: Ошибка при конфигурации или установке пакета (dpkg)" | |
| error "" | |
| error " Решение:" | |
| error " sudo dpkg --configure -a" | |
| error " sudo apt --fix-broken install" | |
| error " # Принудительная конфигурация:" | |
| error " sudo dpkg --force-configure-any --configure -a" | |
| error " # Крайний случай — удалить сломанный пакет:" | |
| error " sudo dpkg --remove --force-remove-reinstreq <пакет>" | |
| return | |
| fi | |
| error "" | |
| error "❓ Автоматическая диагностика не определила причину." | |
| error "" | |
| error " Что делать:" | |
| error " 1. Посмотри хвост лога: tail -50 $LOG_FILE" | |
| error " 2. Выполни:" | |
| error " sudo dpkg --configure -a" | |
| error " sudo apt --fix-broken install" | |
| error " sudo apt update" | |
| error " 3. Запусти скрипт снова" | |
| } | |
| explain_do_release_error() { | |
| local log_tail | |
| log_tail=$(tail -60 "$LOG_FILE" 2>/dev/null || true) | |
| error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| error " do-release-upgrade завершился с ошибкой" | |
| error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| if echo "$log_tail" | grep -q "No new release found"; then | |
| error "" | |
| error "🔍 ПРИЧИНА: Новый релиз не найден" | |
| error "" | |
| error " Решение:" | |
| error " cat /etc/update-manager/release-upgrades # проверь Prompt=lts" | |
| error " sudo sed -i 's/Prompt=.*/Prompt=lts/' /etc/update-manager/release-upgrades" | |
| error " sudo do-release-upgrade # повторить" | |
| error " Или принудительно:" | |
| error " sudo do-release-upgrade -d" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -qE "unresolvable|could not calculate|dependency|conflicts"; then | |
| error "" | |
| error "💥 ПРИЧИНА: Конфликт зависимостей при расчёте апгрейда" | |
| error " Обычно из-за сторонних PPA или нестандартных пакетов" | |
| error "" | |
| error " Решение:" | |
| error " 1. Детальный лог: cat /var/log/dist-upgrade/apt.log | tail -80" | |
| error " 2. Убедись, что PPA отключены (скрипт делает это автоматически)" | |
| error " 3. Если мешает конкретный пакет:" | |
| error " sudo apt-get remove <проблемный-пакет>" | |
| error " 4. Запусти скрипт снова" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -q "held back"; then | |
| error "" | |
| error "📌 ПРИЧИНА: Удержанные пакеты блокируют апгрейд" | |
| error "" | |
| error " Решение:" | |
| error " sudo apt-mark showhold" | |
| error " sudo apt-mark unhold <пакет>" | |
| error " sudo apt full-upgrade" | |
| error " Затем запусти скрипт снова" | |
| return | |
| fi | |
| if echo "$log_tail" | grep -qE "Authentication|GPG|NO_PUBKEY"; then | |
| error "" | |
| error "🔑 ПРИЧИНА: Ошибка GPG-ключа репозитория" | |
| error "" | |
| error " Решение:" | |
| error " sudo apt-key list # посмотреть ключи (deprecated)" | |
| error " sudo apt update 2>&1 | grep 'NO_PUBKEY'" | |
| error " # Добавить ключ:" | |
| error " sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys <KEY_ID>" | |
| return | |
| fi | |
| error "" | |
| error "❓ Причина не определена автоматически. Смотри логи:" | |
| error " Основной лог: $LOG_FILE" | |
| error " Лог апгрейда: /var/log/dist-upgrade/apt.log" | |
| error " Детальный лог: /var/log/dist-upgrade/main.log" | |
| error "" | |
| error " Попробуй запустить вручную для интерактивного режима:" | |
| error " sudo do-release-upgrade" | |
| } | |
| # ─── Безопасный запуск apt ─────────────────────────────────────────────────── | |
| run_apt() { | |
| local desc="$1"; shift | |
| info "▶ $desc" | |
| local rc=0 | |
| DEBIAN_FRONTEND=noninteractive "$@" \ | |
| -o Dpkg::Options::="--force-confdef" \ | |
| -o Dpkg::Options::="--force-confold" \ | |
| 2>&1 | tee -a "$LOG_FILE" || rc=$? | |
| if (( rc != 0 )); then | |
| explain_apt_error "$rc" | |
| die "$desc завершился с ошибкой (код $rc)" | |
| fi | |
| ok "$desc — готово" | |
| } | |
| # ─── Обновить текущую систему ───────────────────────────────────────────────── | |
| update_current_system() { | |
| step "Обновление текущей системы" | |
| run_apt "apt-get update" apt-get update | |
| run_apt "apt-get upgrade" apt-get upgrade -y | |
| run_apt "apt-get dist-upgrade" apt-get dist-upgrade -y | |
| run_apt "apt-get autoremove" apt-get autoremove -y --purge | |
| apt-get clean 2>&1 | tee -a "$LOG_FILE" || true | |
| ok "Система полностью обновлена" | |
| } | |
| # ─── do-release-upgrade ─────────────────────────────────────────────────────── | |
| do_upgrade() { | |
| local target="$1" | |
| step "Запуск do-release-upgrade → Ubuntu $target" | |
| apt-get install -y update-manager-core 2>&1 | tee -a "$LOG_FILE" || true | |
| # Установить Prompt=lts | |
| if [[ -f /etc/update-manager/release-upgrades ]]; then | |
| sed -i 's/Prompt=.*/Prompt=lts/' /etc/update-manager/release-upgrades | |
| fi | |
| local rc=0 | |
| DEBIAN_FRONTEND=noninteractive do-release-upgrade \ | |
| -f DistUpgradeViewNonInteractive \ | |
| 2>&1 | tee -a "$LOG_FILE" || rc=$? | |
| if (( rc != 0 )); then | |
| explain_do_release_error | |
| die "do-release-upgrade → Ubuntu $target завершился с ошибкой (код $rc)" | |
| fi | |
| ok "do-release-upgrade → Ubuntu $target — завершён успешно" | |
| } | |
| # ─── Перезагрузка с сохранением состояния ──────────────────────────────────── | |
| do_reboot() { | |
| local next_state="$1" | |
| set_state "$next_state" | |
| echo "" | |
| step "Перезагрузка системы" | |
| info "После перезагрузки запусти скрипт снова:" | |
| echo -e " ${BLD}sudo bash $SCRIPT_PATH${RST}" | |
| echo "" | |
| info "Перезагрузка через 10 секунд... (Ctrl+C — отменить)" | |
| sleep 10 | |
| reboot | |
| } | |
| # ─── MAIN ───────────────────────────────────────────────────────────────────── | |
| main() { | |
| check_root | |
| init_dirs | |
| local ver | |
| ver=$(get_ubuntu_version) | |
| local state | |
| state=$(get_state) | |
| echo -e "\n${BLD}${CYN}╔══════════════════════════════════════════════╗${RST}" | |
| echo -e "${BLD}${CYN}║ Ubuntu Upgrade Helper 20.04 → 24.04 ║${RST}" | |
| echo -e "${BLD}${CYN}╚══════════════════════════════════════════════╝${RST}\n" | |
| info "Версия: Ubuntu $ver | Состояние: $state" | |
| info "Лог: $LOG_FILE" | |
| echo "" | |
| case "$ver" in | |
| "20.04") | |
| step "Этап 1/2: Ubuntu 20.04 → 22.04 LTS (Jammy Jellyfish)" | |
| # Защита от повторного входа после неудачного апгрейда | |
| if [[ "$state" == "after_upgrade_20_22" ]]; then | |
| error "Состояние указывает, что апгрейд до 22.04 был запущен," | |
| error "но мы всё ещё на 20.04." | |
| error "" | |
| error "Возможные причины:" | |
| error " - Апгрейд прервался (прочитай: /var/log/dist-upgrade/main.log)" | |
| error " - do-release-upgrade завершился, но не перезагрузился" | |
| error "" | |
| error "Попробуй вручную: sudo do-release-upgrade" | |
| die "Неожиданное состояние. Проверь логи." | |
| fi | |
| preflight_checks "22.04" | |
| take_snapshot "pre-upgrade-20-to-22" | |
| disable_ppas | |
| update_current_system | |
| do_upgrade "22.04" | |
| do_reboot "after_upgrade_20_22" | |
| ;; | |
| "22.04") | |
| step "Этап 2/2: Ubuntu 22.04 → 24.04 LTS (Noble Numbat)" | |
| preflight_checks "24.04" | |
| take_snapshot "pre-upgrade-22-to-24" | |
| disable_ppas | |
| update_current_system | |
| do_upgrade "24.04" | |
| do_reboot "after_upgrade_22_24" | |
| ;; | |
| "24.04") | |
| step "Ubuntu 24.04 LTS — цель достигнута!" | |
| restore_ppas | |
| set_state "complete" | |
| update_current_system | |
| echo "" | |
| echo -e "${BLD}${GRN}╔══════════════════════════════════════════════╗${RST}" | |
| echo -e "${BLD}${GRN}║ ✅ Апгрейд успешно завершён! ║${RST}" | |
| echo -e "${BLD}${GRN}║ Ubuntu $(lsb_release -rs) $(lsb_release -cs) ║${RST}" | |
| echo -e "${BLD}${GRN}╚══════════════════════════════════════════════╝${RST}" | |
| echo "" | |
| info "Что проверить после апгрейда:" | |
| info " 1. Сторонние PPA — проверь совместимость:" | |
| info " ls /etc/apt/sources.list.d/" | |
| info " 2. Драйверы: ubuntu-drivers devices" | |
| info " 3. Работу сети, принтеров, VPN" | |
| info " 4. Полный лог: $LOG_FILE" | |
| ;; | |
| *) | |
| die "Неожиданная версия Ubuntu: $ver. Скрипт поддерживает 20.04, 22.04 и 24.04." | |
| ;; | |
| esac | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment