SHA2562c6f60417d45ada6e06f5a7aa4884bcc439528ee35e8499328218f19516907ddVerify after downloading: echo "2c6f60417d45ada6e06f5a7aa4884bcc439528ee35e8499328218f19516907dd arch-update.sh" | sha256sum -c
Read it first, then run it. The complete source is further down on this page. There is deliberately no curl … | bash one-liner here.
Use at your own risk — no liability for data loss or system damage. See the terms of use.
What the script is for
On Arch-based systems, a full update usually happens in two steps: official repos via pacman, AUR packages via a helper like yay. Over time, AUR packages that were pulled in as dependencies pile up and end up unused. Without regularly checking, they just sit there.
What it does
Updates system and AUR packages via an AUR helper (default: yay).
Finds installed packages that don't come from official repos (presumably AUR).
Splits them into "still needed" and "not required by anything anymore"
(Required By: None).
Offers to remove the ones that are no longer needed.
For the AUR packages still needed, checks whether they can even still be found
(AUR, or meanwhile in official repos) — and warns if not.
Runs as a dry run by default; changes only happen with --apply.
How it works
The interesting part is how the script determines "AUR-only": it compares the list of installed foreign packages against what the AUR helper knows as AUR packages.
LC_ALL=C isn't a minor detail here, it's necessary: pacman -Qi only outputs field names like "Required By" consistently in English. If the system is set to a different language, parsing the dependency info would silently produce wrong results — exactly the kind of bug you only notice after something needed has already been removed.
Why I automated this
Because AUR-only packages quietly pile up over time, and otherwise I'd only go through them by hand every once in a while.
Usage
bash arch-update.sh --help
bash arch-update.sh # dry run, only shows what would happen
bash arch-update.sh --apply # actually runs the update and cleanup
Source
arch-update.sh
#!/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 <<EOF
${SCRIPT_NAME} ${VERSION}
System- und AUR-Update anstoßen, dabei verwaiste AUR-only-Pakete finden
und optional entfernen.
Verwendung:
${SCRIPT_NAME} [OPTIONEN]
Optionen:
--apply Änderungen wirklich ausführen (Update, Entfernen).
Ohne diese Option läuft alles nur als Trockenlauf.
--dry-run Nur anzeigen, was passieren würde (Standard).
--no-update System-/AUR-Update überspringen.
--no-cleanup Suche nach verwaisten AUR-only-Paketen überspringen.
--no-check Prüfung noch benötigter AUR-Pakete überspringen.
--aur-helper=PROGRAMM AUR-Helfer verwenden (Standard: yay).
--no-color Ausgabe ohne Farbcodes (z. B. für Cron/Logs).
-h, --help Diese Hilfe anzeigen.
--version Version anzeigen.
Beispiele:
${SCRIPT_NAME} --help
${SCRIPT_NAME} # zeigt nur, was passieren würde
${SCRIPT_NAME} --apply # führt Update und Aufräumen wirklich aus
${SCRIPT_NAME} --apply --no-update # nur aufräumen, kein Systemupdate
${SCRIPT_NAME} --aur-helper=paru --apply
Exit-Codes:
0 = alles gut
1 = Problem gefunden (z. B. AUR-Paket nirgends mehr auffindbar)
2 = Aufruffehler
EOF
}
# ---- Argumente --------------------------------------------------------
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) dry_run=0 ;;
--dry-run) dry_run=1 ;;
--no-update) do_update=0 ;;
--no-cleanup) do_cleanup=0 ;;
--no-check) do_check=0 ;;
--aur-helper=*) aur_helper="${1#*=}" ;;
--no-color) use_color=0 ;;
-h|--help) usage; exit 0 ;;
--version) printf '%s %s\n' "$SCRIPT_NAME" "$VERSION"; exit 0 ;;
*)
err "Unbekannte Option: $1"
usage
exit 2
;;
esac
shift
done
}
# ---- Voraussetzungen ----------------------------------------------------
check_prerequisites() {
if ! command -v pacman >/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 "$@"