Primer crearem l’archiu de l’script:
touch install-and-uninstall.sh
Despres anirem a dins de l’archiu
nano install-and-uninstall.sh
Dins tindrem que afegir el seguent codi:
#!/bin/bash
# ----------------------------------------------------------------------------
# Script: install-and-uninstall.sh
# Descripción:
# - Gestiona la instalación y desinstalación de paquetes mediante menús
# organizados en categorías y subcategorías (usando solo echo/read).
# - Las listas de paquetes para instalación están extendidas, y al seleccionar
# un paquete se muestra una descripción breve y se pregunta al usuario si
# está seguro de proceder.
# - La categoría "Externos" se maneja con procedimientos especiales en la instalación,
# mientras que en la desinstalación se descarta.
# - Incluye registro de operaciones, manejo de errores mejorado, opción de actualización
# del sistema, búsqueda de paquetes, soporte para Snap/Flatpak y modo de simulación.
# ----------------------------------------------------------------------------
# -------------------------------------
# Definición de colores ANSI (más variedad)
# -------------------------------------
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # Sin color
# -------------------------------------
# Configuración del log
# -------------------------------------
LOG_FILE="/var/log/gestor-paquetes.log"
DRY_RUN=false # Variable para el modo simulación
# -------------------------------------
# Función para registrar actividad
# -------------------------------------
log_message() {
local type="$1" # INFO, WARNING, ERROR, ACTION, CANCEL
local message="$2"
echo "$(date '+%Y-%m-%d %H:%M:%S') [$type] $message" | sudo tee -a "$LOG_FILE" > /dev/null
}
# ----------------------------------------------------------------------------
# Creación del archivo .desktop global y su copia en los escritorios
# ----------------------------------------------------------------------------
DESKTOP_FILE="/usr/share/applications/gestor-paquetes.desktop"
self_path=$(readlink -f "$0")
echo -e "${BLUE}Creando archivo .desktop global...${NC}"
if [ "$(id -u)" -eq 0 ]; then
cat <<EOF > "$DESKTOP_FILE"
[Desktop Entry]
Version=1.0
Type=Application
Name=Gestor de Paquetes
Comment=Ejecuta el script de instalación/desinstalación de paquetes
Exec=$self_path
Icon=utilities-terminal
Terminal=true
EOF
log_message INFO "Archivo .desktop creado en $DESKTOP_FILE"
else
echo -e "${YELLOW}Se requieren permisos de root para crear el .desktop. Solicitando elevación...${NC}"
log_message WARNING "Intentando crear .desktop con sudo."
sudo bash -c "cat <<EOF > $DESKTOP_FILE
[Desktop Entry]
Version=1.0
Type=Application
Name=Gestor de Paquetes
Comment=Ejecuta el script de instalación/desinstalación de paquetes
Exec=$self_path
Icon=utilities-terminal
Terminal=true
EOF"
if [ $? -eq 0 ]; then
log_message INFO "Archivo .desktop creado en $DESKTOP_FILE (con sudo)."
else
log_message ERROR "Fallo al crear el archivo .desktop en $DESKTOP_FILE."
fi
fi
echo -e "${GREEN}Archivo .desktop creado/actualizado en: $DESKTOP_FILE${NC}"
desktop_dir="$(xdg-user-dir DESKTOP 2>/dev/null)"
if [ -z "$desktop_dir" ]; then
desktop_dir="$HOME/Desktop"
fi
if [ -d "$desktop_dir" ]; then
if [ "$DRY_RUN" = false ]; then
cp "$DESKTOP_FILE" "$desktop_dir/"
log_message INFO "Acceso directo copiado al escritorio del usuario actual: $desktop_dir"
else
echo -e "${YELLOW}Simulación: cp \"$DESKTOP_FILE\" \"$desktop_dir/\"${NC}"
fi
echo -e "${GREEN}Acceso directo copiado al escritorio del usuario actual: $desktop_dir${NC}"
else
echo -e "${YELLOW}No se encontró el directorio de escritorio para el usuario actual.${NC}"
log_message WARNING "No se encontró el directorio de escritorio para el usuario actual."
fi
for user_home in /home/*; do
if [ -d "$user_home" ]; then
if [ -d "$user_home/Desktop" ]; then
if [ "$DRY_RUN" = false ]; then
sudo cp "$DESKTOP_FILE" "$user_home/Desktop/"
log_message INFO "Acceso directo copiado en: $user_home/Desktop"
else
echo -e "${YELLOW}Simulación: sudo cp \"$DESKTOP_FILE\" \"$user_home/Desktop/\"${NC}"
fi
echo -e "${CYAN}Acceso directo copiado en: $user_home/Desktop${NC}"
elif [ -d "$user_home/Escritorio" ]; then
if [ "$DRY_RUN" = false ]; then
sudo cp "$DESKTOP_FILE" "$user_home/Escritorio/"
log_message INFO "Acceso directo copiado en: $user_home/Escritorio"
else
echo -e "${YELLOW}Simulación: sudo cp \"$DESKTOP_FILE\" \"$user_home/Escritorio/\"${NC}"
fi
echo -e "${CYAN}Acceso directo copiado en: $user_home/Escritorio${NC}"
fi
fi
done
# ----------------------------------------------------------------------------
# Funciones de apoyo para sugerir correcciones en nombres
# ----------------------------------------------------------------------------
filtrar_paquete() {
local input="$1"
shift
local lista=("$@")
local suggestions=()
for pkg in "${lista[@]}"; do
if [[ "${pkg,,}" == *"${input,,}"* ]]; then
suggestions+=("$pkg")
fi
done
echo "${suggestions[@]}"
}
verificar_paquete() {
local input="$1"
shift
local lista=("$@")
local found=0
for pkg in "${lista[@]}"; do
if [[ "${pkg,,}" == "${input,,}" ]]; then
found=1
break
fi
done
if [ $found -eq 0 ]; then
suggestions=( $(filtrar_paquete "$input" "${lista[@]}") )
if [ ${#suggestions[@]} -gt 0 ]; then
echo -e "${YELLOW}No se encontró \"$input\" exactamente. Quizás quisiste decir:${NC}"
local index=1
for s in "${suggestions[@]}"; do
echo -e " $index) $s"
((index++))
done
read -p "Elige la opción (ingresa el número) o escribe nuevamente el nombre: " resp
if [[ "$resp" =~ ^[0-9]+$ ]]; then
input=${suggestions[$((resp-1))]}
else
input="$resp"
fi
else
echo -e "${YELLOW}No se encontraron sugerencias para '$input'. Se usará la entrada tal cual.${NC}"
fi
fi
echo "$input"
}
# ----------------------------------------------------------------------------
# Configuración de categorías para INSTALACIÓN (listas extendidas)
# ----------------------------------------------------------------------------
declare -A categorias
# Categoría Herramientas
categorias["Herramientas"]="Utilidades de Terminal|nano vim emacs mc htop glances ranger net-tools curl wget rsync tree ncdu inxi neofetch bat ripgrep fd-find
Automatización y Scripting|ansible terraform cron apt-xapian-index build-essential"
# Categoría Desarrollo
categorias["Desarrollo"]="Lenguajes de Programación|python3 python3-pip gcc g++ openjdk-17-jdk nodejs npm ruby-full perl golang rustc cargo swift-lang php php-cli php-mysql php-fpm
Control de Versiones|git subversion mercurial
IDE y Editores|gedit kate geany sublime-text atom codeblocks eclipse netbeans intellijidea
Bases de Datos|mysql-server postgresql sqlite3 redis-server mongodb-org-server"
# Categoría Redes
categorias["Redes"]="Monitoreo y Diagnóstico|nmap wireshark-qt tcpdump traceroute iperf3 netcat openvpn telnet dnsutils whois
Seguridad y Firewall|ufw fail2ban openssh-server gufw clamav rkhunter chkrootkit john"
# Categoría Multimedia
categorias["Multimedia"]="Reproductores y Codecs|vlc totem audacious rhythmbox ffmpeg gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly
Edición de Audio|audacity ardour lmms sox easytag
Edición de Video|kdenlive openshot obs-studio handbrake vokoscreen krita
Edición de Imagen y Diseño|gimp inkscape krita darktable blender digikam pinta imagemagick"
# Categoría Oficina y Productividad
categorias["Oficina y Productividad"]="Suites de Oficina|libreoffice libreoffice-writer libreoffice-calc libreoffice-impress libreoffice-draw
Utilidades PDF|evince okular qpdfview poppler-utils
Gestión de Proyectos|planner projectlibre taskwarrior"
# Categoría Utilidades del Sistema
categorias["Utilidades del Sistema"]="Archivado y Compresión|p7zip-full unrar zip unzip xz-utils bzip2 gzip tar
Backup y Recuperación|grsync timeshift deja-dup rsnapshot testdisk foremost
Virtualización|virtualbox virt-manager qemu-kvm vagrant docker.io docker-compose"
# Categoría Navegadores Web
categorias["Navegadores Web"]="Navegadores|firefox chromium-browser opera-stable brave-browser" # opera-stable and brave-browser usually installed via external sources
# Categoría Juegos y Emuladores
categorias["Juegos y Emuladores"]="Juegos|steam lutris playonlinux minetest openarena
Emuladores|retroarch pcsx2 dolphin-emu mgba-qt mupen64plus"
# Categoría Externos (procedimientos especiales para .deb o PPAs)
categorias["Externos"]="Software Externo|google-chrome-stable code slack spotify zoom discord opera brave-browser teamviewer postman" # Added more examples
# ----------------------------------------------------------------------------
# Array asociativo con descripciones para cada paquete (simplificado para APT, detallado para externos)
# ----------------------------------------------------------------------------
declare -A descripciones
# Descripciones genéricas para paquetes APT (se usará apt-cache show para detalles)
descripciones["nano"]="Editor de texto de terminal."
descripciones["vim"]="Editor de texto programable."
descripciones["emacs"]="Editor de texto extensible."
descripciones["mc"]="Midnight Commander: gestor de archivos."
descripciones["htop"]="Monitor de procesos interactivo."
descripciones["glances"]="Monitor de sistema en tiempo real."
descripciones["ranger"]="Gestor de archivos de terminal."
descripciones["net-tools"]="Herramientas de red básicas."
descripciones["curl"]="Herramienta para transferir datos con URL."
descripciones["wget"]="Descargador de archivos."
descripciones["rsync"]="Herramienta de sincronización y backup."
descripciones["tree"]="Muestra el contenido del directorio en forma de árbol."
descripciones["ncdu"]="Analizador de uso de disco en terminal."
descripciones["inxi"]="Herramienta de información del sistema."
descripciones["neofetch"]="Muestra la información del sistema en un ASCII art."
descripciones["bat"]="Un 'cat' con esteroides, con resaltado de sintaxis."
descripciones["ripgrep"]="Herramienta de búsqueda de texto superrápida."
descripciones["fd-find"]="Una alternativa más rápida y amigable a 'find'."
descripciones["ansible"]="Plataforma de automatización IT."
descripciones["terraform"]="Herramienta de infraestructura como código."
descripciones["cron"]="Daemon para ejecutar comandos programados."
descripciones["apt-xapian-index"]="Índice de búsqueda para paquetes APT."
descripciones["build-essential"]="Paquetes esenciales para compilar software."
descripciones["python3"]="Lenguaje de programación Python 3."
descripciones["python3-pip"]="Gestor de paquetes para Python 3."
descripciones["gcc"]="Compilador de GNU C."
descripciones["g++"]="Compilador de GNU C++."
descripciones["openjdk-17-jdk"]="Kit de desarrollo de Java OpenJDK 17."
descripciones["nodejs"]="Entorno de ejecución de JavaScript."
descripciones["npm"]="Gestor de paquetes para Node.js."
descripciones["ruby-full"]="Entorno completo de Ruby."
descripciones["perl"]="Lenguaje de scripting Perl."
descripciones["golang"]="Lenguaje de programación Go."
descripciones["rustc"]="Compilador de Rust."
descripciones["cargo"]="Gestor de paquetes de Rust."
descripciones["swift-lang"]="Compilador de Swift."
descripciones["php"]="Lenguaje de scripting PHP."
descripciones["php-cli"]="Versión de línea de comandos de PHP."
descripciones["php-mysql"]="Módulo MySQL para PHP."
descripciones["php-fpm"]="FastCGI Process Manager para PHP."
descripciones["git"]="Sistema de control de versiones distribuido."
descripciones["subversion"]="Sistema de control de versiones centralizado."
descripciones["mercurial"]="Sistema de control de versiones distribuido."
descripciones["gedit"]="Editor de texto simple para GNOME."
descripciones["kate"]="Editor de texto avanzado de KDE."
descripciones["geany"]="Entorno de desarrollo ligero."
descripciones["sublime-text"]="Editor de texto y código propietario."
descripciones["atom"]="Editor de texto personalizable."
descripciones["codeblocks"]="IDE para C, C++ y Fortran."
descripciones["eclipse"]="IDE extensible para Java y otros."
descripciones["netbeans"]="IDE para Java, PHP, HTML, etc."
descripciones["intellijidea"]="IDE para desarrollo Java y Kotlin (versión de comunidad)."
descripciones["mysql-server"]="Servidor de base de datos MySQL."
descripciones["postgresql"]="Servidor de base de datos PostgreSQL."
descripciones["sqlite3"]="Motor de base de datos SQL incrustado."
descripciones["redis-server"]="Servidor de base de datos en memoria."
descripciones["mongodb-org-server"]="Servidor de base de datos NoSQL MongoDB."
descripciones["nmap"]="Escáner de red y auditor de seguridad."
descripciones["wireshark-qt"]="Analizador de protocolos de red gráfico."
descripciones["tcpdump"]="Herramienta de captura de tráfico de red."
descripciones["traceroute"]="Utilidad para rastrear rutas de red."
descripciones["iperf3"]="Herramienta de medición de rendimiento de red."
descripciones["openvpn"]="Software de VPN."
descripciones["telnet"]="Cliente de Telnet."
descripciones["dnsutils"]="Utilidades de DNS (dig, nslookup)."
descripciones["whois"]="Cliente Whois."
descripciones["ufw"]="Firewall simple y fácil de usar."
descripciones["fail2ban"]="Previene ataques de fuerza bruta."
descripciones["openssh-server"]="Servidor SSH."
descripciones["gufw"]="Interfaz gráfica para UFW."
descripciones["clamav"]="Antivirus de línea de comandos."
descripciones["rkhunter"]="Escáner de rootkits."
descripciones["chkrootkit"]="Detector de rootkits."
descripciones["john"]="Crackeador de contraseñas (John the Ripper)."
descripciones["vlc"]="Reproductor multimedia versátil."
descripciones["totem"]="Reproductor de video para GNOME."
descripciones["audacious"]="Reproductor de audio ligero."
descripciones["rhythmbox"]="Reproductor de música para GNOME."
descripciones["ffmpeg"]="Marco de trabajo para audio/video."
descripciones["gstreamer1.0-plugins-good"]="Plugins multimedia GStreamer (buenos)."
descripciones["gstreamer1.0-plugins-bad"]="Plugins multimedia GStreamer (malos)."
descripciones["gstreamer1.0-plugins-ugly"]="Plugins multimedia GStreamer (feos)."
descripciones["audacity"]="Editor de audio."
descripciones["ardour"]="Estación de trabajo de audio digital (DAW)."
descripciones["lmms"]="Estación de trabajo musical Linux."
descripciones["sox"]="Navaja suiza de los efectos de sonido."
descripciones["easytag"]="Editor de etiquetas para audio."
descripciones["kdenlive"]="Editor de video no lineal."
descripciones["openshot"]="Editor de video simple."
descripciones["obs-studio"]="Software para streaming y grabación."
descripciones["handbrake"]="Transcodificador de video."
descripciones["vokoscreen"]="Grabador de pantalla."
descripciones["gimp"]="Editor de imágenes."
descripciones["inkscape"]="Editor de gráficos vectoriales."
descripciones["krita"]="Software de pintura digital."
descripciones["darktable"]="Procesador de imágenes RAW."
descripciones["blender"]="Suite de creación 3D."
descripciones["digikam"]="Gestor de fotos."
descripciones["pinta"]="Editor de imágenes similar a Paint.NET."
descripciones["imagemagick"]="Suite de herramientas de manipulación de imágenes."
descripciones["libreoffice"]="Suite ofimática completa."
descripciones["libreoffice-writer"]="Procesador de textos de LibreOffice."
descripciones["libreoffice-calc"]="Hoja de cálculo de LibreOffice."
descripciones["libreoffice-impress"]="Software de presentación de LibreOffice."
descripciones["libreoffice-draw"]="Herramienta de dibujo de LibreOffice."
descripciones["evince"]="Visor de documentos (PDF, PostScript)."
descripciones["okular"]="Visor de documentos universal de KDE."
descripciones["qpdfview"]="Visor de PDF ligero."
descripciones["poppler-utils"]="Utilidades para PDFs."
descripciones["planner"]="Herramienta de gestión de proyectos."
descripciones["projectlibre"]="Software de gestión de proyectos compatible con MS Project."
descripciones["taskwarrior"]="Gestor de tareas basado en línea de comandos."
descripciones["p7zip-full"]="Archivador 7-Zip."
descripciones["unrar"]="Extractor de archivos RAR."
descripciones["zip"]="Archivador Zip."
descripciones["unzip"]="Extractor de archivos Zip."
descripciones["xz-utils"]="Utilidades de compresión XZ."
descripciones["bzip2"]="Compresor de archivos de alta calidad."
descripciones["gzip"]="Compresor de archivos de uso común."
descripciones["tar"]="Utilidad de archivado de cintas."
descripciones["grsync"]="Interfaz gráfica para rsync."
descripciones["timeshift"]="Herramienta de restauración del sistema."
descripciones["deja-dup"]="Herramienta de backup simple."
descripciones["rsnapshot"]="Herramienta de backup basado en rsync."
descripciones["testdisk"]="Herramienta de recuperación de datos."
descripciones["foremost"]="Herramienta de recuperación de archivos."
descripciones["virtualbox"]="Software de virtualización."
descripciones["virt-manager"]="Gestor de máquinas virtuales."
descripciones["qemu-kvm"]="Emulador de virtualización."
descripciones["vagrant"]="Herramienta para construir y gestionar entornos de máquinas virtuales."
descripciones["docker.io"]="Plataforma de contenedores Docker."
descripciones["docker-compose"]="Herramienta para definir y ejecutar aplicaciones Docker multi-contenedor."
descripciones["firefox"]="Navegador web de Mozilla."
descripciones["chromium-browser"]="Navegador web de código abierto."
descripciones["steam"]="Plataforma de distribución digital de videojuegos."
descripciones["lutris"]="Gestor de juegos para Linux."
descripciones["playonlinux"]="Herramienta para ejecutar juegos de Windows en Linux."
descripciones["minetest"]="Juego tipo Minecraft de código abierto."
descripciones["openarena"]="Juego de disparos en primera persona."
descripciones["retroarch"]="Frontend para emuladores de juegos."
descripciones["pcsx2"]="Emulador de PlayStation 2."
descripciones["dolphin-emu"]="Emulador de GameCube y Wii."
descripciones["mgba-qt"]="Emulador de Game Boy Advance."
descripciones["mupen64plus"]="Emulador de Nintendo 64."
# Externos - Software Externo (más detalladas y con enlaces actuales donde sea posible)
descripciones["google-chrome-stable"]="Google Chrome: Navegador web rápido y seguro, de Google."
descripciones["code"]="Visual Studio Code: Editor de código moderno, extensible y multiplataforma (Microsoft)."
descripciones["slack"]="Slack: Herramienta de comunicación y colaboración para equipos."
descripciones["spotify"]="Spotify: Servicio de música en streaming con amplio catálogo y alta calidad."
descripciones["zoom"]="Zoom: Plataforma de videoconferencia que facilita reuniones virtuales."
descripciones["discord"]="Discord: Plataforma con chat de voz y texto para comunidades y gamers."
descripciones["opera"]="Opera: Navegador web innovador con características únicas y VPN integrada."
descripciones["brave-browser"]="Brave Browser: Navegador web enfocado en la privacidad y con bloqueador de anuncios incorporado."
descripciones["teamviewer"]="TeamViewer: Software de acceso remoto y colaboración online."
descripciones["postman"]="Postman: Plataforma de API para construir, probar y documentar APIs."
# ----------------------------------------------------------------------------
# Función para instalar paquetes (APT, Snap, Flatpak)
# ----------------------------------------------------------------------------
install_package() {
local package_name="$1"
local install_type="$2" # apt, snap, flatpak, external
local category="$3" # Para logging
log_message ACTION "Iniciando instalación de $package_name (tipo: $install_type) en categoría $category."
echo -e "\n${CYAN}Instalando ${GREEN}$package_name${NC} a través de $install_type..."
local cmd=""
local success_message=""
local error_message=""
local post_install_info=""
local pkg_found=false
# Pre-check if package exists in a sensible way for each type
if [ "$install_type" == "apt" ]; then
if apt-cache show "$package_name" &>/dev/null; then
pkg_found=true
fi
elif [ "$install_type" == "snap" ]; then
if snap find "$package_name" &>/dev/null; then
pkg_found=true
fi
elif [ "$install_type" == "flatpak" ]; then
if flatpak search "$package_name" &>/dev/null; then # This might return many, a better check is flatpak remote-ls flathub | grep $package_name
pkg_found=true # Simplistic check for now
fi
elif [ "$install_type" == "external" ]; then
if [[ -n "${descripciones[$package_name]}" ]]; then # If it has a custom description, assume it's "known"
pkg_found=true
fi
fi
if [ "$pkg_found" = false ]; then
echo -e "${RED}Error: El paquete '$package_name' no se encontró o no es válido para el tipo de instalación '$install_type'.${NC}"
log_message ERROR "Paquete '$package_name' no encontrado/válido para $install_type."
return 1
fi
case "$install_type" in
apt)
cmd="sudo apt update && sudo apt install -y $package_name"
success_message="¡Instalación de $package_name (APT) completada!"
error_message="Error al instalar $package_name (APT). Intenta 'sudo apt update --fix-missing' o 'sudo apt --fix-broken install'."
;;
snap)
cmd="sudo snap install $package_name"
success_message="¡Instalación de $package_name (Snap) completada!"
error_message="Error al instalar $package_name (Snap). Asegúrate de que Snapd esté instalado ('sudo apt install snapd') y que el paquete exista."
;;
flatpak)
cmd="sudo flatpak install -y flathub $package_name" # Asumimos flathub como remoto
success_message="¡Instalación de $package_name (Flatpak) completada!"
error_message="Error al instalar $package_name (Flatpak). Asegúrate de que Flatpak esté instalado y Flathub configurado (ver https://flatpak.org/setup/)."
;;
external)
# Manejo de paquetes externos (APT personalizado vía .deb, PPA, etc.)
case "$package_name" in
google-chrome-stable)
cmd="wget -O /tmp/google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && sudo dpkg -i /tmp/google-chrome.deb && sudo apt-get -f install -y"
success_message="¡Instalación de Google Chrome completada!"
error_message="Error al instalar Google Chrome. Posibles problemas de dependencia. Intenta 'sudo apt-get -f install'."
;;
code)
cmd="wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/packages.microsoft.gpg && sudo install -o root -g root -m 644 /tmp/packages.microsoft.gpg /etc/apt/trusted.gpg.d/ && sudo sh -c 'echo \"deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/repos/vscode stable main\" > /etc/apt/sources.list.d/vscode.list' && sudo apt update && sudo apt install -y code"
success_message="¡Instalación de VS Code completada!"
error_message="Error al instalar VS Code. Revisa las fuentes de paquetes."
;;
slack)
cmd="wget -O /tmp/slack.deb https://downloads.slack-edge.com/linux_releases/slack-desktop-4.33.73-amd64.deb && sudo dpkg -i /tmp/slack.deb && sudo apt-get -f install -y"
success_message="¡Instalación de Slack completada!"
error_message="Error al instalar Slack. Posibles problemas de dependencia. Intenta 'sudo apt-get -f install'."
;;
spotify)
# Updated Spotify installation from their official guide
cmd="curl -sS https://download.spotify.com/debian/pubkey_7A3A762FAFD4A51F.gpg | sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/spotify.gpg && echo \"deb http://repository.spotify.com stable non-free\" | sudo tee /etc/apt/sources.list.d/spotify.list > /dev/null && sudo apt update && sudo apt install -y spotify-client"
success_message="¡Instalación de Spotify completada!"
error_message="Error al instalar Spotify. Revisa las claves GPG y las fuentes de paquetes."
;;
zoom)
cmd="wget -O /tmp/zoom.deb https://zoom.us/client/latest/zoom_amd64.deb && sudo dpkg -i /tmp/zoom.deb && sudo apt-get -f install -y"
success_message="¡Instalación de Zoom completada!"
error_message="Error al instalar Zoom. Posibles problemas de dependencia. Intenta 'sudo apt-get -f install'."
;;
discord)
cmd="wget -O /tmp/discord.deb \"https://discordapp.com/api/download?platform=linux&format=deb\" && sudo dpkg -i /tmp/discord.deb && sudo apt-get -f install -y"
success_message="¡Instalación de Discord completada!"
error_message="Error al instalar Discord. Posibles problemas de dependencia. Intenta 'sudo apt-get -f install'."
;;
opera)
# Updated Opera installation from their official guide
cmd="curl -fsSL https://deb.opera.com/archive.key | sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/opera.gpg && echo \"deb [arch=amd64] https://deb.opera.com/opera-stable/ stable non-free\" | sudo tee /etc/apt/sources.list.d/opera.list > /dev/null && sudo apt update && sudo apt install -y opera-stable"
success_message="¡Instalación de Opera completada!"
error_message="Error al instalar Opera. Revisa las claves GPG y las fuentes de paquetes."
;;
brave-browser)
# Brave Browser installation from their official guide
cmd="sudo curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg && echo \"deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main\"|sudo tee /etc/apt/sources.list.d/brave-browser-release.list > /dev/null && sudo apt update && sudo apt install -y brave-browser"
success_message="¡Instalación de Brave Browser completada!"
error_message="Error al instalar Brave Browser. Revisa las claves GPG y las fuentes de paquetes."
;;
teamviewer)
cmd="wget -O /tmp/teamviewer.deb https://download.teamviewer.com/download/linux/teamviewer_amd64.deb && sudo dpkg -i /tmp/teamviewer.deb && sudo apt-get -f install -y"
success_message="¡Instalación de TeamViewer completada!"
error_message="Error al instalar TeamViewer. Posibles problemas de dependencia. Intenta 'sudo apt-get -f install'."
;;
postman)
# Postman is typically installed from their tar.gz or via snap, providing a snap option here as apt is not direct
echo -e "${YELLOW}Postman no se instala directamente vía APT. Sugerimos instalarlo como un Snap.${NC}"
read -p "¿Quieres instalar Postman vía Snap? (S/n): " confirm_snap
if [[ "$confirm_snap" =~ ^[Ss]$ ]]; then
install_package "postman" "snap" "Externos (via Snap)"
return $? # Return result of snap install
else
echo -e "${YELLOW}Instalación de Postman cancelada.${NC}"
log_message CANCEL "Instalación de Postman (externo) cancelada por el usuario."
return 1
fi
;;
*)
echo -e "${YELLOW}Paquete externo '$package_name' no reconocido para instalación automática. Se requiere intervención manual.${NC}"
log_message ERROR "Intento de instalación de paquete externo no reconocido: $package_name."
return 1 # Fallo
;;
esac
;;
*)
echo -e "${RED}Tipo de instalación desconocido: $install_type.${NC}"
log_message ERROR "Tipo de instalación desconocido: $install_type para $package_name."
return 1
;;
esac
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: Comando a ejecutar: $cmd${NC}"
log_message INFO "Simulación de instalación de $package_name. Comando: '$cmd'"
echo -e "${GREEN}Simulación de instalación completada para $package_name.${NC}"
return 0 # En modo simulación, siempre 'exitoso'
fi
eval "$cmd"
if [ $? -eq 0 ]; then
echo -e "${GREEN}$success_message${NC}"
log_message INFO "$success_message"
if [ -n "$post_install_info" ]; then
echo -e "${YELLOW}$post_install_info${NC}"
log_message INFO "$post_install_info"
fi
return 0
else
echo -e "${RED}$error_message${NC}"
log_message ERROR "$error_message (Comando: $cmd)"
return 1
fi
}
# ----------------------------------------------------------------------------
# Función para desinstalar paquetes (APT, Snap, Flatpak)
# ----------------------------------------------------------------------------
uninstall_package() {
local package_name="$1"
local install_type="$2" # apt, snap, flatpak
local category="$3" # Para logging
log_message ACTION "Iniciando desinstalación de $package_name (tipo: $install_type) en categoría $category."
echo -e "\n${BLUE}Desinstalando ${RED}$package_name${NC} a través de $install_type..."
local cmd=""
local success_message=""
local error_message=""
case "$install_type" in
apt)
cmd="sudo apt remove -y $package_name"
success_message="¡Desinstalación de $package_name (APT) completada!"
error_message="Error al desinstalar $package_name (APT). Intenta 'sudo apt autoremove' o 'sudo apt --fix-broken install'."
;;
snap)
cmd="sudo snap remove $package_name"
success_message="¡Desinstalación de $package_name (Snap) completada!"
error_message="Error al desinstalar $package_name (Snap)."
;;
flatpak)
cmd="sudo flatpak uninstall -y $package_name"
success_message="¡Desinstalación de $package_name (Flatpak) completada!"
error_message="Error al desinstalar $package_name (Flatpak)."
;;
*)
echo -e "${RED}Tipo de desinstalación desconocido: $install_type.${NC}"
log_message ERROR "Tipo de desinstalación desconocido: $install_type para $package_name."
return 1
;;
esac
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: Comando a ejecutar: $cmd${NC}"
log_message INFO "Simulación de desinstalación de $package_name. Comando: '$cmd'"
echo -e "${GREEN}Simulación de desinstalación completada para $package_name.${NC}"
return 0
fi
eval "$cmd"
if [ $? -eq 0 ]; then
echo -e "${GREEN}$success_message${NC}"
log_message INFO "$success_message"
return 0
else
echo -e "${RED}$error_message${NC}"
log_message ERROR "$error_message (Comando: $cmd)"
return 1
fi
}
# ----------------------------------------------------------------------------
# Función para el menú post-operación
# ----------------------------------------------------------------------------
post_op_menu() {
local menu_choice=""
while true; do
echo -e "\n${MAGENTA}=== Operación Completada. ¿Qué quieres hacer ahora? ===${NC}"
echo -e "${GREEN}1) Actualizar repositorios (apt update)${NC}"
echo -e "${GREEN}2) Actualizar sistema (apt update && apt upgrade)${NC}"
echo -e "${YELLOW}3) Instalar paquetes${NC}"
echo -e "${RED}4) Desinstalar paquetes${NC}"
echo -e "${BLUE}5) Ver registro de operaciones (log)${NC}"
echo -e "${BLUE}6) Ver información detallada de un paquete${NC}"
echo -e "${MAGENTA}7) Configurar opciones personalizadas (próximamente)${NC}" # Placeholder
echo -e "${BLUE}8) Salir del gestor${NC}"
read -p "Elige una opción: " menu_choice
log_message INFO "Opción seleccionada en menú post-operación: $menu_choice"
case "$menu_choice" in
1)
echo -e "${CYAN}Actualizando repositorios (apt update)...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: sudo apt update${NC}"
log_message INFO "Simulación: Actualizando repositorios."
else
sudo apt update
if [ $? -eq 0 ]; then
echo -e "${GREEN}Repositorios actualizados.${NC}"
log_message INFO "Repositorios actualizados correctamente."
else
echo -e "${RED}Error al actualizar repositorios.${NC}"
log_message ERROR "Error al actualizar repositorios."
fi
fi
;;
2)
echo -e "${CYAN}Actualizando el sistema (apt update && apt upgrade)...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: sudo apt update && sudo apt upgrade -y${NC}"
log_message INFO "Simulación: Actualización completa del sistema."
else
sudo apt update && sudo apt upgrade -y
if [ $? -eq 0 ]; then
echo -e "${GREEN}Sistema actualizado.${NC}"
log_message INFO "Sistema actualizado correctamente."
else
echo -e "${RED}Error al actualizar el sistema. Revisa el log para más detalles.${NC}"
log_message ERROR "Error al actualizar el sistema."
fi
fi
;;
3)
# Redirigir al inicio del menú principal para instalación
return 0 # Exit post_op_menu to return to main_menu loop
;;
4)
# Redirigir al inicio del menú principal para desinstalación
return 0 # Exit post_op_menu to return to main_menu loop
;;
5)
echo -e "\n${CYAN}Mostrando las últimas 50 líneas del registro de operaciones (${LOG_FILE}):${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: Mostrar log de operaciones. (En modo real: tail -n 50 $LOG_FILE)${NC}"
log_message INFO "Simulación: Solicitado ver log."
else
if [ -f "$LOG_FILE" ]; then
sudo tail -n 50 "$LOG_FILE"
else
echo -e "${YELLOW}El archivo de log aún no existe: $LOG_FILE${NC}"
fi
fi
read -p "Presiona Enter para continuar..."
;;
6)
echo -e "\n${CYAN}Búsqueda de información detallada de un paquete (APT/Snap/Flatpak):${NC}"
read -p "Ingresa el nombre exacto del paquete: " pkg_name_info
log_message INFO "Solicitud de información detallada para paquete: $pkg_name_info"
if [ -z "$pkg_name_info" ]; then
echo -e "${RED}Nombre de paquete vacío.${NC}"
continue
fi
echo -e "\n${BLUE}Buscando información APT para '$pkg_name_info'...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: apt-cache show $pkg_name_info${NC}"
else
apt-cache show "$pkg_name_info"
fi
echo -e "\n${BLUE}Buscando información Snap para '$pkg_name_info'...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: snap info $pkg_name_info${NC}"
else
snap info "$pkg_name_info" 2>/dev/null
fi
echo -e "\n${BLUE}Buscando información Flatpak para '$pkg_name_info'...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: flatpak info $pkg_name_info${NC}"
else
flatpak info "$pkg_name_info" 2>/dev/null
fi
read -p "Presiona Enter para continuar..."
;;
7)
echo -e "${YELLOW}Esta función está en desarrollo. ¡Vuelve pronto!${NC}"
log_message INFO "Accedido a función 'Configurar opciones personalizadas' (en desarrollo)."
;;
8)
echo -e "\n${MAGENTA}Saliendo del script.${NC}"
log_message INFO "Saliendo del script principal."
exit 0 # Exit the entire script
;;
*)
echo -e "${RED}Opción no válida. Por favor, elige una opción entre 1 y 8.${NC}"
log_message WARNING "Opción no válida en menú post-operación: $menu_choice"
;;
esac
done
}
# ----------------------------------------------------------------------------
# Función principal para la gestión de paquetes
# ----------------------------------------------------------------------------
main_menu() {
local exit_script=0
log_message INFO "Iniciando el script 'Gestor de Paquetes'."
# Ensure log file exists and is writable by root
if [ ! -f "$LOG_FILE" ]; then
sudo touch "$LOG_FILE"
sudo chmod 644 "$LOG_FILE"
fi
while [ "$exit_script" -eq 0 ]; do
echo -e "\n${MAGENTA}=== Gestor de Paquetes ===${NC}"
echo -e "${CYAN}Modo de Simulación (Dry-Run): ${YELLOW}$DRY_RUN${NC}"
echo -e "${GREEN}1) Instalar un paquete (APT, Snap, Flatpak o Externo)${NC}"
echo -e "${RED}2) Desinstalar un paquete (APT, Snap o Flatpak)${NC}"
echo -e "${BLUE}3) Actualizar sistema (apt update && apt upgrade)${NC}"
echo -e "${YELLOW}4) Buscar un paquete${NC}"
echo -e "${MAGENTA}5) Alternar modo de simulación (Dry-Run)${NC}"
echo -e "${BLUE}6) Salir${NC}"
read -p "Elige una opción (1-6): " opcion
log_message INFO "Opción seleccionada en menú principal: $opcion"
case "$opcion" in
1) # Instalación
log_message ACTION "Iniciando proceso de instalación."
echo -e "\n${CYAN}Tipo de instalación:${NC}"
echo -e " ${GREEN}1) Paquete APT (desde repositorios de Ubuntu)${NC}"
echo -e " ${YELLOW}2) Paquete Snap${NC}"
echo -e " ${BLUE}3) Paquete Flatpak${NC}"
echo -e " ${MAGENTA}4) Paquete Externo (procesamiento especial, e.g., Chrome, VS Code)${NC}"
read -p "Elige el tipo de instalación (1-4): " install_type_choice
local selected_install_type=""
local paquetes_to_process=()
local categoria_para_log=""
case "$install_type_choice" in
1) # APT
selected_install_type="apt"
echo -e "\n${CYAN}Categorías disponibles (APT):${NC}"
categorias_keys=()
i=1
for key in "${!categorias[@]}"; do
if [ "$key" != "Externos" ]; then # No mostrar externos aquí
echo -e " ${YELLOW}$i) $key${NC}"
categorias_keys+=("$key")
((i++))
fi
done
read -p "Elige una categoría (ingresa el número): " cat_choice
if ! [[ "$cat_choice" =~ ^[0-9]+$ ]] || [ "$cat_choice" -lt 1 ] || [ "$cat_choice" -gt ${#categorias_keys[@]} ]; then
echo -e "${RED}Selección de categoría no válida.${NC}"
log_message WARNING "Selección de categoría APT no válida: $cat_choice"
continue
fi
categoria_seleccionada=${categorias_keys[$((cat_choice-1))]}
categoria_para_log="$categoria_seleccionada (APT)"
echo -e "\n${CYAN}Subcategorías en ${categoria_seleccionada}:${NC}"
subcats=$(echo -e "${categorias[$categoria_seleccionada]}")
IFS=$'\n' read -d '' -r -a subcategorias_array < <(echo -e "$subcats")
i=1
for sub in "${subcategorias_array[@]}"; do
subcat_name=$(echo "$sub" | cut -d'|' -f1)
echo -e " ${YELLOW}$i) $subcat_name${NC}"
((i++))
done
read -p "Elige una subcategoría (número): " sub_choice
if ! [[ "$sub_choice" =~ ^[0-9]+$ ]] || [ "$sub_choice" -lt 1 ] || [ "$sub_choice" -gt ${#subcategorias_array[@]} ]; then
echo -e "${RED}Selección de subcategoría no válida.${NC}"
log_message WARNING "Selección de subcategoría APT no válida: $sub_choice"
continue
fi
selected_line="${subcategorias_array[$((sub_choice-1))]}"
selected_subcat=$(echo "$selected_line" | cut -d'|' -f1)
paquetes_str=$(echo "$selected_line" | cut -d'|' -f2)
read -a paquetes_array <<< "$paquetes_str"
echo -e "\n${CYAN}Paquetes disponibles en ${categoria_seleccionada} -> ${selected_subcat}:${NC}"
i=1
for pkg in "${paquetes_array[@]}"; do
echo -e " ${YELLOW}$i) $pkg${NC}"
((i++))
done
read -p "Elige el paquete a instalar (ingresa el número o escribe el nombre): " entrada
if [[ "$entrada" =~ ^[0-9]+$ ]]; then
if [ "$entrada" -lt 1 ] || [ "$entrada" -gt ${#paquetes_array[@]} ]; then
echo -e "${RED}Número de paquete no válido.${NC}"
log_message WARNING "Número de paquete APT no válido: $entrada"
continue 2 # Exit this inner case and outer case
fi
paquete_seleccionado=${paquetes_array[$((entrada-1))]}
else
paquete_seleccionado=$(verificar_paquete "$entrada" "${paquetes_array[@]}")
fi
paquetes_to_process=("$paquete_seleccionado")
;;
2) # Snap
selected_install_type="snap"
read -p "Introduce el nombre del paquete Snap a instalar (e.g., 'spotify', 'vlc'): " pkg_snap
if [ -z "$pkg_snap" ]; then
echo -e "${RED}Nombre de paquete Snap vacío.${NC}"
log_message WARNING "Nombre de paquete Snap vacío."
continue
fi
paquetes_to_process=("$pkg_snap")
categoria_para_log="Snap"
;;
3) # Flatpak
selected_install_type="flatpak"
read -p "Introduce el ID de la aplicación Flatpak a instalar (e.g., 'org.kde.krita', 'com.spotify.Client'): " pkg_flatpak
if [ -z "$pkg_flatpak" ]; then
echo -e "${RED}ID de aplicación Flatpak vacío.${NC}"
log_message WARNING "ID de aplicación Flatpak vacío."
continue
fi
paquetes_to_process=("$pkg_flatpak")
categoria_para_log="Flatpak"
;;
4) # Externos
selected_install_type="external"
echo -e "\n${CYAN}Paquetes Externos disponibles:${NC}"
subcats_ext=$(echo -e "${categorias["Externos"]}")
# Ensure subcats_ext is not empty before proceeding
if [ -z "$subcats_ext" ]; then
echo -e "${RED}No se encontraron paquetes externos definidos.${NC}"
log_message ERROR "No se encontraron paquetes externos definidos."
continue
fi
selected_line_ext="${subcats_ext}" # Asume que solo hay una línea para externos
paquetes_str_ext=$(echo "$selected_line_ext" | cut -d'|' -f2)
read -a paquetes_array_ext <<< "$paquetes_str_ext"
i=1
for pkg in "${paquetes_array_ext[@]}"; do
echo -e " ${YELLOW}$i) $pkg${NC}"
((i++))
done
read -p "Elige el paquete externo a instalar (ingresa el número o escribe el nombre): " entrada_ext
if [[ "$entrada_ext" =~ ^[0-9]+$ ]]; then
if [ "$entrada_ext" -lt 1 ] || [ "$entrada_ext" -gt ${#paquetes_array_ext[@]} ]; then
echo -e "${RED}Número de paquete externo no válido.${NC}"
log_message WARNING "Número de paquete externo no válido: $entrada_ext"
continue 2 # Exit this inner case and outer case
fi
paquete_seleccionado=${paquetes_array_ext[$((entrada_ext-1))]}
else
paquete_seleccionado=$(verificar_paquete "$entrada_ext" "${paquetes_array_ext[@]}")
fi
paquetes_to_process=("$paquete_seleccionado")
categoria_para_log="Externos"
;;
*)
echo -e "${RED}Opción de tipo de instalación no válida.${NC}"
log_message WARNING "Opción de tipo de instalación no válida: $install_type_choice."
continue
;;
esac
if [ ${#paquetes_to_process[@]} -eq 0 ] || [ -z "${paquetes_to_process[0]}" ]; then
echo -e "${RED}No se seleccionó ningún paquete para instalar.${NC}"
log_message CANCEL "Instalación cancelada: ningún paquete seleccionado."
continue
fi
for paquete_a_instalar in "${paquetes_to_process[@]}"; do
# Mostrar descripción y solicitar confirmación
echo -e "\n${CYAN}Obteniendo descripción del paquete...${NC}"
local description=""
if [ "$selected_install_type" == "apt" ]; then
description=$(apt-cache show "$paquete_a_instalar" 2>/dev/null | awk -F': ' '/Description:/{print $2; exit}')
if [ -z "$description" ]; then
description="No hay descripción disponible en el repositorio APT."
fi
elif [ "$selected_install_type" == "snap" ]; then
description=$(snap info "$paquete_a_instalar" 2>/dev/null | grep -A 1 summary: | tail -n 1 | sed 's/^ *//')
if [ -z "$description" ]; then
description="No hay descripción disponible para este Snap o no está instalado."
fi
elif [ "$selected_install_type" == "flatpak" ]; then
description=$(flatpak info "$paquete_a_instalar" 2>/dev/null | grep -A 1 Description: | tail -n 1 | sed 's/^ *//')
if [ -z "$description" ]; then
description="No hay descripción disponible para este Flatpak o no está instalado."
fi
elif [ "$selected_install_type" == "external" ]; then
description=${descripciones[$paquete_a_instalar]}
if [ -z "$description" ]; then
description="No hay descripción disponible para este paquete externo."
fi
fi
echo -e "${CYAN}Descripción:${NC} $description"
read -p "¿Estás seguro de instalar el paquete '$paquete_a_instalar' (tipo: $selected_install_type)? (S/n): " confirm
if ! [[ "$confirm" =~ ^[Ss]$ || -z "$confirm" ]]; then
echo -e "${YELLOW}Instalación de '$paquete_a_instalar' cancelada.${NC}"
log_message CANCEL "Instalación de '$paquete_a_instalar' cancelada por el usuario."
continue
fi
install_package "$paquete_a_instalar" "$selected_install_type" "$categoria_para_log"
done
post_op_menu
;;
2) # Desinstalación
log_message ACTION "Iniciando proceso de desinstalación."
echo -e "\n${CYAN}Tipo de desinstalación:${NC}"
echo -e " ${GREEN}1) Paquete APT${NC}"
echo -e " ${YELLOW}2) Paquete Snap${NC}"
echo -e " ${BLUE}3) Paquete Flatpak${NC}"
read -p "Elige el tipo de desinstalación (1-3): " uninstall_type_choice
local selected_uninstall_type=""
local paquete_a_desinstalar=""
local categoria_para_log_uninstall=""
case "$uninstall_type_choice" in
1) # APT
selected_uninstall_type="apt"
categoria_para_log_uninstall="APT"
echo -e "\n${CYAN}Recabando información de paquetes APT instalados según la clasificación...${NC}"
declare -A inst_categorias
for cat in "${!categorias[@]}"; do
if [ "$cat" == "Externos" ]; then
continue # Los externos se desinstalan con apt si fueron instalados via deb
fi
subcats=$(echo -e "${categorias[$cat]}")
IFS=$'\n' read -d '' -r -a subs_array < <(echo -e "$subcats")
result=""
for sub in "${subs_array[@]}"; do
subcat_name=$(echo "$sub" | cut -d'|' -f1)
pkg_list=$(echo "$sub" | cut -d'|' -f2)
pkg_array=($pkg_list)
installed_packages=()
for pkg in "${pkg_array[@]}"; do
if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
installed_packages+=("$pkg")
fi
done
if [ ${#installed_packages[@]} -gt 0 ]; then
result+="${subcat_name}|$(echo ${installed_packages[@]})"$'\n'
fi
done
if [ -n "$result" ]; then
inst_categorias["$cat"]="$result"
fi
done
if [ ${#inst_categorias[@]} -eq 0 ]; then
echo -e "${RED}No se encontraron paquetes APT instalados en las categorías predefinidas.${NC}"
log_message INFO "No se encontraron paquetes APT predefinidos instalados para desinstalar."
continue
fi
echo -e "\n${CYAN}Categorías disponibles para desinstalación (APT):${NC}"
available_categories=()
i=1
for cat in "${!inst_categorias[@]}"; do
echo -e " ${YELLOW}$i) $cat${NC}"
available_categories+=("$cat")
((i++))
done
read -p "Elige una categoría (número): " cat_choice_inst
if ! [[ "$cat_choice_inst" =~ ^[0-9]+$ ]] || [ "$cat_choice_inst" -lt 1 ] || [ "$cat_choice_inst" -gt ${#available_categories[@]} ]; then
echo -e "${RED}Selección de categoría no válida.${NC}"
log_message WARNING "Selección de categoría APT para desinstalación no válida: $cat_choice_inst"
continue
fi
chosen_cat="${available_categories[$((cat_choice_inst-1))]}"
echo -e "\n${CYAN}Subcategorías en ${chosen_cat}:${NC}"
subcats_inst=$(echo -e "${inst_categorias[$chosen_cat]}")
IFS=$'\n' read -d '' -r -a subs_inst_array < <(echo -e "$subcats_inst")
i=1
for sub in "${subs_inst_array[@]}"; do
subcat_inst=$(echo "$sub" | cut -d'|' -f1)
echo -e " ${YELLOW}$i) $subcat_inst${NC}"
((i++))
done
read -p "Elige una subcategoría (número): " sub_choice_inst
if ! [[ "$sub_choice_inst" =~ ^[0-9]+$ ]] || [ "$sub_choice_inst" -lt 1 ] || [ "$sub_choice_inst" -gt ${#subs_inst_array[@]} ]; then
echo -e "${RED}Selección de subcategoría no válida.${NC}"
log_message WARNING "Selección de subcategoría APT para desinstalación no válida: $sub_choice_inst"
continue
fi
selected_line_inst="${subs_inst_array[$((sub_choice_inst-1))]}"
selected_subcat_inst=$(echo "$selected_line_inst" | cut -d'|' -f1)
pkg_line_inst=$(echo "$selected_line_inst" | cut -d'|' -f2)
read -a pkg_array_inst <<< "$pkg_line_inst"
echo -e "\n${CYAN}Paquetes instalados en ${chosen_cat} -> ${selected_subcat_inst}:${NC}"
i=1
for pkg in "${pkg_array_inst[@]}"; do
echo -e " ${YELLOW}$i) $pkg${NC}"
((i++))
done
read -p "Elige el paquete a desinstalar (ingresa el número o escribe el nombre): " entrada
if [[ "$entrada" =~ ^[0-9]+$ ]]; then
if [ "$entrada" -lt 1 ] || [ "$entrada" -gt ${#pkg_array_inst[@]} ]; then
echo -e "${RED}Número de paquete no válido.${NC}"
log_message WARNING "Número de paquete APT para desinstalación no válido: $entrada"
continue 2 # Exit this inner case and outer case
fi
paquete_a_desinstalar=${pkg_array_inst[$((entrada-1))]}
else
paquete_a_desinstalar=$(verificar_paquete "$entrada" "${pkg_array_inst[@]}")
fi
;;
2) # Snap Uninstall
selected_uninstall_type="snap"
categoria_para_log_uninstall="Snap"
echo -e "\n${CYAN}Paquetes Snap instalados:${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: snap list${NC}"
snap_list_output="spotify\ncode" # Mock data
else
snap_list_output=$(snap list | awk 'NR>1 {print $1}')
fi
if [ -z "$snap_list_output" ]; then
echo -e "${YELLOW}No se encontraron paquetes Snap instalados.${NC}"
log_message INFO "No se encontraron paquetes Snap instalados para desinstalar."
continue
fi
read -a snap_installed_array <<< "$snap_list_output"
i=1
for pkg in "${snap_installed_array[@]}"; do
echo -e " ${YELLOW}$i) $pkg${NC}"
((i++))
done
read -p "Elige el paquete Snap a desinstalar (número o nombre): " snap_entry
if [[ "$snap_entry" =~ ^[0-9]+$ ]]; then
if [ "$snap_entry" -lt 1 ] || [ "$snap_entry" -gt ${#snap_installed_array[@]} ]; then
echo -e "${RED}Número de paquete Snap no válido.${NC}"
log_message WARNING "Número de paquete Snap para desinstalación no válido: $snap_entry"
continue 2
fi
paquete_a_desinstalar=${snap_installed_array[$((snap_entry-1))]}
else
paquete_a_desinstalar=$(verificar_paquete "$snap_entry" "${snap_installed_array[@]}")
fi
;;
3) # Flatpak Uninstall
selected_uninstall_type="flatpak"
categoria_para_log_uninstall="Flatpak"
echo -e "\n${CYAN}Aplicaciones Flatpak instaladas:${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: flatpak list${NC}"
flatpak_list_output="org.kde.krita\ncom.spotify.Client" # Mock data
else
flatpak_list_output=$(flatpak list --app --columns=application | awk 'NR>1 {print $1}')
fi
if [ -z "$flatpak_list_output" ]; then
echo -e "${YELLOW}No se encontraron aplicaciones Flatpak instaladas.${NC}"
log_message INFO "No se encontraron Flatpak instalados para desinstalar."
continue
fi
read -a flatpak_installed_array <<< "$flatpak_list_output"
i=1
for pkg in "${flatpak_installed_array[@]}"; do
echo -e " ${YELLOW}$i) $pkg${NC}"
((i++))
done
read -p "Elige la aplicación Flatpak a desinstalar (número o ID): " flatpak_entry
if [[ "$flatpak_entry" =~ ^[0-9]+$ ]]; then
if [ "$flatpak_entry" -lt 1 ] || [ "$flatpak_entry" -gt ${#flatpak_installed_array[@]} ]; then
echo -e "${RED}Número de aplicación Flatpak no válido.${NC}"
log_message WARNING "Número de aplicación Flatpak para desinstalación no válido: $flatpak_entry"
continue 2
fi
paquete_a_desinstalar=${flatpak_installed_array[$((flatpak_entry-1))]}
else
paquete_a_desinstalar=$(verificar_paquete "$flatpak_entry" "${flatpak_installed_array[@]}")
fi
;;
*)
echo -e "${RED}Opción de tipo de desinstalación no válida.${NC}"
log_message WARNING "Opción de tipo de desinstalación no válida: $uninstall_type_choice."
continue
;;
esac
if [ -z "$paquete_a_desinstalar" ]; then
echo -e "${RED}No se seleccionó ningún paquete para desinstalar.${NC}"
log_message CANCEL "Desinstalación cancelada: ningún paquete seleccionado."
continue
fi
read -p "¿Estás seguro de desinstalar el paquete '$paquete_a_desinstalar' (tipo: $selected_uninstall_type)? (S/n): " confirm_un
if ! [[ "$confirm_un" =~ ^[Ss]$ || -z "$confirm_un" ]]; then
echo -e "${YELLOW}Desinstalación de '$paquete_a_desinstalar' cancelada.${NC}"
log_message CANCEL "Desinstalación de '$paquete_a_desinstalar' cancelada por el usuario."
continue
fi
uninstall_package "$paquete_a_desinstalar" "$selected_uninstall_type" "$categoria_para_log_uninstall"
post_op_menu
;;
3) # Actualizar sistema
log_message ACTION "Iniciando actualización completa del sistema."
echo -e "${CYAN}Actualizando el sistema (apt update && apt upgrade)...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: sudo apt update && sudo apt upgrade -y${NC}"
log_message INFO "Simulación: Actualización completa del sistema."
else
sudo apt update && sudo apt upgrade -y
if [ $? -eq 0 ]; then
echo -e "${GREEN}Sistema actualizado.${NC}"
log_message INFO "Sistema actualizado correctamente."
else
echo -e "${RED}Error al actualizar el sistema. Revisa el log para más detalles.${NC}"
log_message ERROR "Error al actualizar el sistema."
fi
fi
post_op_menu
;;
4) # Buscar un paquete
log_message ACTION "Iniciando búsqueda de paquetes."
echo -e "\n${CYAN}=== Búsqueda de Paquetes ===${NC}"
read -p "Introduce el término de búsqueda: " search_term
if [ -z "$search_term" ]; then
echo -e "${RED}Término de búsqueda vacío.${NC}"
log_message WARNING "Búsqueda de paquete: término vacío."
continue
fi
log_message INFO "Buscando paquetes con el término: '$search_term'"
echo -e "\n${BLUE}Resultados de búsqueda APT para '$search_term':${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: apt-cache search $search_term${NC}"
echo -e "${YELLOW} (Muestra ejemplos de resultados APT) ${NC}"
echo "nano - small, friendly text editor"
echo "htop - interactive process viewer"
else
apt-cache search "$search_term" | head -n 20 # Limitar a 20 resultados para no saturar
fi
echo -e "\n${BLUE}Resultados de búsqueda Snap para '$search_term':${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: snap find $search_term${NC}"
echo -e "${YELLOW} (Muestra ejemplos de resultados Snap) ${NC}"
echo "Name Version Summary"
echo "spotify 1.2.3.4 Music streaming service"
echo "vlc 3.0.18 VLC media player"
else
snap find "$search_term" | head -n 20
fi
echo -e "\n${BLUE}Resultados de búsqueda Flatpak para '$search_term':${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${YELLOW}Simulación: flatpak search $search_term${NC}"
echo -e "${YELLOW} (Muestra ejemplos de resultados Flatpak) ${NC}"
echo "Name Application ID Version"
echo "Krita org.kde.krita 5.2.2"
echo "Discord com.discordapp.Discord 1.0.9036"
else
flatpak search "$search_term" | head -n 20
fi
read -p "Presiona Enter para continuar..."
;;
5) # Alternar modo de simulación
if [ "$DRY_RUN" = true ]; then
DRY_RUN=false
echo -e "${GREEN}Modo de Simulación (Dry-Run) DESACTIVADO.${NC}"
log_message INFO "Modo de Simulación DESACTIVADO."
else
DRY_RUN=true
echo -e "${YELLOW}Modo de Simulación (Dry-Run) ACTIVADO. Ningún comando se ejecutará realmente.${NC}"
log_message INFO "Modo de Simulación ACTIVADO."
fi
;;
6) # Salir del script
exit_script=1
echo -e "\n${MAGENTA}Saliendo del script.${NC}"
log_message INFO "Saliendo del script 'Gestor de Paquetes'."
;;
*) # Opción no válida
echo -e "${RED}Opción no válida. Por favor, elige una opción entre 1 y 6.${NC}"
log_message WARNING "Opción no válida en menú principal: $opcion"
;;
esac
done
}
# Iniciar el menú principal
main_menu

Una vegada creat el script tindrem que donar’li permisos:
chmod +x install-and-uninstall.sh
Per ultim executarem una vegada l’escript per que es crei el .desktop:
./install-and-uninstall.sh
