#!/usr/bin/env bash # # arch-update.sh # Zweck: System-/AUR-Update anstoßen und verwaiste AUR-only-Pakete finden. # # Autor: Matthias Meister — https://scripts.web.mm-core.de # Version: 1.0.0 # # Lizenz: MIT — Copyright (c) 2026 Matthias Meister # Volltext: https://scripts.web.mm-core.de/lizenz/ # # Exit-Codes: # 0 = alles gut # 1 = Problem gefunden (z. B. AUR-Paket nirgends mehr auffindbar) # 2 = Aufruffehler (falsche Option, Voraussetzung fehlt) set -euo pipefail readonly SCRIPT_NAME="${0##*/}" readonly VERSION="1.0.0" # ---- Defaults ----------------------------------------------------------- dry_run=1 # Trockenlauf ist Standard, siehe --apply do_update=1 do_cleanup=1 do_check=1 use_color=1 aur_helper="yay" exit_code=0 # ---- Ausgabe -------------------------------------------------------------- setup_colors() { if [[ "$use_color" -eq 1 && -t 1 ]]; then c_info=$'\033[36m'; c_warn=$'\033[33m'; c_err=$'\033[31m'; c_ok=$'\033[32m'; c_off=$'\033[0m' else c_info=""; c_warn=""; c_err=""; c_ok=""; c_off="" fi } info() { printf '%s[Info]%s %s\n' "${c_info}" "${c_off}" "$*"; } ok() { printf '%s[OK]%s %s\n' "${c_ok}" "${c_off}" "$*"; } warn() { printf '%s[Warnung]%s %s\n' "${c_warn}" "${c_off}" "$*" >&2; } err() { printf '%s[Fehler]%s %s\n' "${c_err}" "${c_off}" "$*" >&2; } usage() { cat </dev/null 2>&1; then err "pacman wurde nicht gefunden. Dieses Skript ist für Arch-basierte Systeme gedacht." exit 2 fi if [[ "$do_update" -eq 1 || "$do_cleanup" -eq 1 || "$do_check" -eq 1 ]]; then if ! command -v "$aur_helper" >/dev/null 2>&1; then err "AUR-Helfer '${aur_helper}' wurde nicht gefunden." err "Installieren oder anderen Helfer mit --aur-helper=PROGRAMM angeben." exit 2 fi fi } # ---- System-/AUR-Update -------------------------------------------------- do_system_update() { info "Aktualisiere System- und AUR-Pakete mit '${aur_helper} -Syu' ..." if [[ "$dry_run" -eq 1 ]]; then info "Trockenlauf: würde ausführen: ${aur_helper} -Syu" return 0 fi "$aur_helper" -Syu ok "Update abgeschlossen." } # ---- Verwaiste AUR-only-Pakete finden ------------------------------------ # Vergleicht installierte "fremde" Pakete (nicht in offiziellen Repos, also # vermutlich aus dem AUR) mit dem, was der AUR-Helfer als AUR-Paketliste # kennt. LC_ALL=C erzwingt englische, stabil parsbare pacman-Ausgabe - # sonst hängt das Parsen von "Required By" von der Systemsprache ab. find_aur_only_packages() { local -a installed_foreign aur_known aur_only mapfile -t installed_foreign < <(LC_ALL=C pacman -Qqm | sort) if [[ "${#installed_foreign[@]}" -eq 0 ]]; then info "Keine fremden (nicht-offiziellen) Pakete installiert." return 0 fi mapfile -t aur_known < <(LC_ALL=C "$aur_helper" -Slq | sort) mapfile -t aur_only < <( comm -23 \ <(printf '%s\n' "${installed_foreign[@]}") \ <(printf '%s\n' "${aur_known[@]}") ) if [[ "${#aur_only[@]}" -eq 0 ]]; then info "Keine AUR-only-Pakete gefunden." return 0 fi info "AUR-only-Pakete gefunden (${#aur_only[@]}):" printf ' %s\n' "${aur_only[@]}" local -a unneeded=() needed=() local pkg required_by for pkg in "${aur_only[@]}"; do required_by=$(LC_ALL=C pacman -Qi "$pkg" | awk -F': ' '/^Required By/ {print $2; exit}') if [[ "$required_by" == "None" ]]; then unneeded+=("$pkg") else needed+=("$pkg") fi done if [[ "$do_cleanup" -eq 1 ]]; then cleanup_unneeded "${unneeded[@]}" fi if [[ "$do_check" -eq 1 ]]; then check_needed "${needed[@]}" fi } # ---- Nicht mehr benötigte AUR-Pakete entfernen --------------------------- cleanup_unneeded() { local -a unneeded=("$@") if [[ "${#unneeded[@]}" -eq 0 ]]; then info "Keine unbenötigten AUR-Pakete (ohne Required By)." return 0 fi info "Unbenötigte AUR-Pakete (von nichts abhängig):" printf ' %s\n' "${unneeded[@]}" if [[ "$dry_run" -eq 1 ]]; then info "Trockenlauf: würde entfernen mit: pacman -Rns ${unneeded[*]}" return 0 fi # pacman -Rns fragt selbst noch einmal interaktiv nach - das bleibt als # zweite Sicherheitsstufe zusätzlich zu --apply bestehen. sudo pacman -Rns "${unneeded[@]}" ok "Entfernt: ${unneeded[*]}" } # ---- Noch benötigte AUR-Pakete prüfen ------------------------------------ check_needed() { local -a needed=("$@") if [[ "${#needed[@]}" -eq 0 ]]; then info "Keine noch benötigten AUR-Pakete zu prüfen." return 0 fi info "Noch benötigte AUR-Pakete - Verfügbarkeit prüfen:" local pkg for pkg in "${needed[@]}"; do local found_aur=0 found_repo=0 LC_ALL=C "$aur_helper" -Ss "^${pkg}$" 2>/dev/null | grep -q "^aur/${pkg} " && found_aur=1 || true LC_ALL=C pacman -Ss "^${pkg}$" 2>/dev/null | grep -q "^extra/${pkg} " && found_repo=1 || true if [[ "$found_aur" -eq 1 ]]; then ok " ${pkg}: weiterhin im AUR verfügbar." elif [[ "$found_repo" -eq 1 ]]; then ok " ${pkg}: mittlerweile in offiziellen Repos verfügbar." else warn " ${pkg}: weder im AUR noch in offiziellen Repos gefunden." warn " -> GitHub prüfen oder manuell neu bauen." exit_code=1 fi done } main() { parse_args "$@" setup_colors check_prerequisites if [[ "$dry_run" -eq 1 ]]; then info "Trockenlauf aktiv (Standard). Für echte Änderungen: --apply" fi if [[ "$do_update" -eq 1 ]]; then do_system_update fi if [[ "$do_cleanup" -eq 1 || "$do_check" -eq 1 ]]; then find_aur_only_packages fi exit "$exit_code" } main "$@"