#!/usr/bin/env bash # Script Name: dockernet-inspector.v1.sh # Beschreibung: Dockernet Inspector - Verwaltet und inspiziert Docker-Netzwerke # - Alle Netzwerke auflisten # - Details zu einem spezifischen Docker-Netzwerk anzeigen # - Belegte IPs je Netzwerk und netzwerkuebergreifend ausgeben # - Ungenutzte Netzwerke (ohne Container) anzeigen # - Netzwerke interaktiv auswaehlen und loeschen # - Alle ungenutzten, loeschbaren Netzwerke auf einmal entfernen # Verwendung: bash ./dockernet-inspector.v1.sh [BEFEHL] [OPTIONS] # Befehle: # networks - Alle Docker Netzwerke auflisten # inspect - Details zu einem Netzwerk anzeigen (Standard) # ips - Belegte IPs in einem Netzwerk # ips-all - Belegte IPs in allen Netzwerken # unused-networks - Netzwerke ohne verbundene Container # delete-networks - Netzwerke interaktiv auswaehlen und loeschen # delete-unused-networks - Alle ungenutzten, loeschbaren Netzwerke loeschen # help, -h, --help - Diese Hilfe anzeigen # Autor: Patrick Asmus # Web: https://www.cleveradmin.de # Git-Reposit.: https://git.techniverse.net/scriptos/dockernet-inspector.git # Version: 1.7.0 # Datum: 08.05.2026 # Modifikation: Sammelloeschung fuer ungenutzte Docker-Netzwerke hinzugefuegt ##################################################### set -uo pipefail TABLE_LINE_WIDTH=80 INTERACTIVE_MODE=false print_rule() { local char="$1" printf '%*s\n' "$TABLE_LINE_WIDTH" '' | tr ' ' "$char" } print_table_title() { local title="$1" echo "$title" print_rule "=" } is_protected_network() { local net="$1" [[ "$net" == "bridge" || "$net" == "host" || "$net" == "none" || "$net" == "ingress" || "$net" == "docker_gwbridge" ]] } # Hilfe-Funktion show_help() { cat < - Details zu einem Netzwerk anzeigen $0 ips - Belegte IPs in einem Netzwerk anzeigen $0 ips-all - Belegte IPs in allen Netzwerken anzeigen $0 unused-networks - Ungenutzte Netzwerke (ohne Container) anzeigen $0 delete-networks - Netzwerke interaktiv auswaehlen und loeschen $0 delete-unused-networks - Alle ungenutzten, loeschbaren Netzwerke loeschen $0 - Alias fuer 'inspect ' $0 help - Diese Hilfe anzeigen BEFEHLE: networks Listet alle verfuegbaren Docker Netzwerke auf ips Zeigt Containername und IPv4-Adresse im angegebenen Netzwerk ips-all Zeigt alle belegten IPv4-Adressen in allen Netzwerken (aufsteigend) unused-networks Zeigt alle Docker Netzwerke ohne verbundene Container delete-networks Netzwerk per Nummer auswaehlen und loeschen; Docker-Standard- und Systemnetzwerke sind geschuetzt delete-unused-networks Loescht alle ungenutzten Docker Netzwerke nach Bestaetigung; bridge, host, none, ingress und docker_gwbridge sind geschuetzt inspect Zeigt detaillierte Informationen zu einem Netzwerk: - Netzwerktyp (Driver) - Bridge-Interface - Subnetz und Gateway - Interface-Status - Netzwerk-Statistiken (RX/TX bytes und packets) help Zeigt diese Hilfe an BEISPIELE: $0 networks $0 ips mynetwork $0 ips-all $0 unused-networks $0 delete-networks $0 delete-unused-networks $0 inspect mynetwork $0 mynetwork $0 -h HELP } # Funktion zum Auflisten aller Docker Netzwerke list_networks() { local table_output header print_table_title "Verfuegbare Docker Netzwerke:" table_output="$(docker network ls --format "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.Scope}}")" || { echo "Fehler: Docker Netzwerke konnten nicht abgerufen werden." exit 2 } header="$(printf "%s\n" "$table_output" | head -n 1)" printf "%s\n" "$header" print_rule "-" printf "%s\n" "$table_output" | tail -n +2 } print_dynamic_table() { local data="$1" local formatted local header if command -v column >/dev/null 2>&1; then formatted="$(printf "%b" "$data" | column -t -s $'\t')" else formatted="$(printf "%b" "$data")" fi header="$(printf "%s\n" "$formatted" | head -n 1)" printf "%s\n" "$header" print_rule "-" printf "%s\n" "$formatted" | tail -n +2 } list_ips_in_network() { local net="$1" local rows table_data rows="$(docker network inspect "$net" --format '{{range $id, $c := .Containers}}{{println $c.Name $c.IPv4Address}}{{end}}' 2>/dev/null || true)" [[ -z "$rows" ]] && { echo "Keine Container mit IPv4-Adresse im Netzwerk '$net' gefunden." exit 0 } print_table_title "Belegte IP-Adressen im Netzwerk '$net':" table_data=$'CONTAINER\tIP\n' while read -r cname ipcidr; do [[ -z "${cname:-}" || -z "${ipcidr:-}" ]] && continue table_data+="${cname}"$'\t'"${ipcidr%%/*}"$'\n' done < <(printf "%s\n" "$rows" | sort -k2,2V) print_dynamic_table "$table_data" } list_ips_all_networks() { local nets rows table_data rows="" nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)" [[ -z "$nets" ]] && { echo "Fehler: Docker Netzwerke konnten nicht abgerufen werden." exit 2 } while read -r net; do [[ -z "${net:-}" ]] && continue while read -r cname ipcidr; do [[ -z "${cname:-}" || -z "${ipcidr:-}" ]] && continue rows+="${net}"$'\t'"${cname}"$'\t'"${ipcidr%%/*}"$'\n' done < <(docker network inspect "$net" --format '{{range $id, $c := .Containers}}{{println $c.Name $c.IPv4Address}}{{end}}' 2>/dev/null || true) done < <(printf "%s\n" "$nets") [[ -z "$rows" ]] && { echo "Keine belegten IPv4-Adressen in Docker Netzwerken gefunden." exit 0 } print_table_title "Belegte IP-Adressen in allen Netzwerken (aufsteigend):" table_data=$'NETZWERK\tCONTAINER\tIP\n' while IFS=$'\t' read -r net cname ip; do [[ -z "${net:-}" || -z "${cname:-}" || -z "${ip:-}" ]] && continue table_data+="${net}"$'\t'"${cname}"$'\t'"${ip}"$'\n' done < <(printf "%s" "$rows" | sort -t $'\t' -k3,3V -k1,1 -k2,2) print_dynamic_table "$table_data" } list_unused_networks() { local nets net containers table_data has_unused nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)" [[ -z "$nets" ]] && { echo "Fehler: Docker Netzwerke konnten nicht abgerufen werden." exit 2 } table_data=$'NAME\tDRIVER\tSCOPE\n' has_unused=false while read -r net; do [[ -z "${net:-}" ]] && continue containers="$(docker network inspect "$net" --format '{{len .Containers}}' 2>/dev/null || true)" if [[ "${containers:-0}" -eq 0 ]]; then local driver scope driver="$(docker network inspect "$net" --format '{{.Driver}}' 2>/dev/null || true)" scope="$(docker network inspect "$net" --format '{{.Scope}}' 2>/dev/null || true)" table_data+="${net}"$'\t'"${driver}"$'\t'"${scope}"$'\n' has_unused=true fi done < <(printf "%s\n" "$nets") if ! $has_unused; then echo "Alle Docker Netzwerke haben mindestens einen verbundenen Container." return 0 fi print_table_title "Ungenutzte Docker Netzwerke (keine Container verbunden):" print_dynamic_table "$table_data" } delete_networks() { local nets nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)" if [[ -z "$nets" ]]; then echo "Fehler: Keine Docker Netzwerke gefunden." return 1 fi local -a net_array mapfile -t net_array <<< "$nets" echo "" echo "Verfuegbare Docker Netzwerke:" echo "" local i for ((i=0; i<${#net_array[@]}; i++)); do local suffix="" is_protected_network "${net_array[$i]}" && suffix=" (geschuetzt - nicht loeschbar)" printf " %d. %s%s\n" "$((i+1))" "${net_array[$i]}" "$suffix" done echo "" read -rp "Nummer des Netzwerks eingeben (0 = Abbrechen): " choice if [[ "$choice" == "0" || -z "$choice" ]]; then echo "Abgebrochen." return 0 fi if ! [[ "$choice" =~ ^[0-9]+$ ]] || ((choice < 1 || choice > ${#net_array[@]})); then echo "Fehler: Ungueltige Nummer." return 1 fi local selected_net="${net_array[$((choice-1))]}" if is_protected_network "$selected_net"; then echo "FEHLER: '$selected_net' ist ein geschuetztes Docker-Netzwerk und kann nicht geloescht werden." return 1 fi echo "" read -rp "Netzwerk '$selected_net' wirklich loeschen? [j/N]: " confirm if [[ "$confirm" != "j" && "$confirm" != "J" ]]; then echo "Abgebrochen." return 0 fi echo "" if docker network rm "$selected_net" 2>/dev/null; then echo "OK: Netzwerk '$selected_net' erfolgreich geloescht." return 0 else echo "FEHLER: Netzwerk '$selected_net' konnte nicht geloescht werden (wird moeglicherweise noch genutzt)." return 1 fi } delete_unused_networks() { local nets net containers driver scope local -a delete_candidates local -a protected_unused nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)" if [[ -z "$nets" ]]; then echo "Fehler: Keine Docker Netzwerke gefunden." return 1 fi delete_candidates=() protected_unused=() while read -r net; do [[ -z "${net:-}" ]] && continue containers="$(docker network inspect "$net" --format '{{len .Containers}}' 2>/dev/null || true)" if [[ "${containers:-0}" -eq 0 ]]; then if is_protected_network "$net"; then protected_unused+=("$net") else delete_candidates+=("$net") fi fi done < <(printf "%s\n" "$nets") if ((${#delete_candidates[@]} == 0)); then echo "Keine ungenutzten, loeschbaren Docker Netzwerke gefunden." if ((${#protected_unused[@]} > 0)); then echo "Geschuetzte ungenutzte Netzwerke werden uebersprungen: ${protected_unused[*]}" fi return 0 fi print_table_title "Ungenutzte Docker Netzwerke, die geloescht werden koennen:" local table_data table_data=$'NAME\tDRIVER\tSCOPE\n' for net in "${delete_candidates[@]}"; do driver="$(docker network inspect "$net" --format '{{.Driver}}' 2>/dev/null || true)" scope="$(docker network inspect "$net" --format '{{.Scope}}' 2>/dev/null || true)" table_data+="${net}"$'\t'"${driver}"$'\t'"${scope}"$'\n' done print_dynamic_table "$table_data" if ((${#protected_unused[@]} > 0)); then echo "" echo "Geschuetzte ungenutzte Netzwerke werden nicht geloescht: ${protected_unused[*]}" fi echo "" read -rp "Alle oben aufgefuehrten ungenutzten Netzwerke wirklich loeschen? [j/N]: " confirm if [[ "$confirm" != "j" && "$confirm" != "J" ]]; then echo "Abgebrochen." return 0 fi local success_count=0 local fail_count=0 echo "" for net in "${delete_candidates[@]}"; do if is_protected_network "$net"; then echo "SKIP: Netzwerk '$net' ist geschuetzt." continue fi if docker network rm "$net" >/dev/null 2>&1; then echo "OK: Netzwerk '$net' erfolgreich geloescht." ((success_count++)) else echo "FEHLER: Netzwerk '$net' konnte nicht geloescht werden (wird moeglicherweise wieder genutzt)." ((fail_count++)) fi done echo "" echo "Ergebnis: $success_count geloescht, $fail_count fehlgeschlagen." ((fail_count == 0)) } # Interaktive Menü-Funktion interactive_menu() { while true; do echo "" print_rule "=" echo "Dockernet Inspector - Interaktives Menü" print_rule "=" echo "" echo "1. Alle Netzwerke auflisten" echo "2. Detaillierte Infos zu einem Netzwerk" echo "3. IPs eines Netzwerks anzeigen" echo "4. Alle belegten IPs anzeigen" echo "5. Ungenutzte Netzwerke anzeigen" echo "6. Netzwerke loeschen" echo "7. Alle ungenutzten Netzwerke loeschen" echo "0. Beenden" echo "" read -rp "Bitte waehlen Sie eine Option (0-7): " choice echo "" case "$choice" in 1) list_networks ;; 2) if select_and_inspect_network; then : fi ;; 3) nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)" if [[ -z "$nets" ]]; then echo "Fehler: Keine Docker Netzwerke gefunden." continue fi echo "Verfuegbare Netzwerke:" select net in $nets; do if [[ -n "$net" ]]; then list_ips_in_network "$net" break else echo "Ungueltige Auswahl. Bitte versuchen Sie es erneut." fi done ;; 4) list_ips_all_networks ;; 5) list_unused_networks ;; 6) delete_networks ;; 7) delete_unused_networks ;; 0) echo "Auf Wiedersehen!" exit 0 ;; *) echo "FEHLER: Ungueltige Eingabe '$choice'. Bitte geben Sie eine Nummer zwischen 0 und 7 ein." sleep 1 ;; esac done } # Funktion zur Auswahl und Inspektion eines Netzwerks select_and_inspect_network() { local nets net nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)" if [[ -z "$nets" ]]; then echo "Fehler: Keine Docker Netzwerke gefunden." return 1 fi echo "Verfuegbare Netzwerke:" select net in $nets; do if [[ -n "$net" ]]; then NET="$net" inspect_network "$NET" return 0 else echo "Ungueltige Auswahl. Bitte versuchen Sie es erneut." fi done } # Separate Funktion fuer Netzwerk-Inspektion inspect_network() { local NET="$1" local DRIVER DRIVER="$(docker network inspect "$NET" --format '{{.Driver}}' 2>/dev/null || true)" [[ -z "$DRIVER" ]] && { echo "Fehler: Netzwerk '$NET' nicht gefunden."; return 2; } echo "" print_rule "=" echo "Detaillerte Informationen fuer Netzwerk: $NET" print_rule "=" echo "" # Sonderbehandlung fuer Docker-Standardnetzwerke if [[ "$NET" == "none" || "$NET" == "host" || "$NET" == "bridge" ]]; then cat </dev/null || true)" BR_RAW="$(docker network inspect "$NET" --format '{{index .Options "com.docker.network.bridge.name"}}' 2>/dev/null || true)" if [[ -z "$BR_RAW" || "$BR_RAW" == "" ]]; then IF="br-${ID:0:12}" else IF="$BR_RAW" fi # 2) Subnet/Gateway SUBNET="$(docker network inspect "$NET" --format '{{(index .IPAM.Config 0).Subnet}}' 2>/dev/null || true)" GATEWAY="$(docker network inspect "$NET" --format '{{(index .IPAM.Config 0).Gateway}}' 2>/dev/null || true)" # 3) Interface-Status & Stats state="unknown"; rxb=0; txb=0; rxp=0; txp=0 if ip link show "$IF" &>/dev/null; then state="$(cat /sys/class/net/"$IF"/operstate 2>/dev/null || echo unknown)" rxb="$(cat /sys/class/net/"$IF"/statistics/rx_bytes 2>/dev/null || echo 0)" txb="$(cat /sys/class/net/"$IF"/statistics/tx_bytes 2>/dev/null || echo 0)" rxp="$(cat /sys/class/net/"$IF"/statistics/rx_packets 2>/dev/null || echo 0)" txp="$(cat /sys/class/net/"$IF"/statistics/tx_packets 2>/dev/null || echo 0)" fi cat <"; exit 1; } list_ips_in_network "$NET" exit 0 ;; ips-all) list_ips_all_networks exit 0 ;; unused-networks) list_unused_networks exit 0 ;; delete-networks) delete_networks exit $? ;; delete-unused-networks) delete_unused_networks exit $? ;; help | -h | --help) show_help exit 0 ;; inspect) NET="${2:-}" [[ -z "$NET" ]] && { echo "Fehler: Netzwerkname erforderlich. Verwendung: $0 inspect "; exit 1; } inspect_network "$NET" exit 0 ;; "") # Starte interaktiven Modus, wenn keine Argumente uebergeben wurden interactive_menu exit 0 ;; *) # Kompatibilitaet: Wenn erstes Argument kein bekannter Befehl ist, behandle es als Netzwerkname NET="$COMMAND" inspect_network "$NET" exit 0 ;; esac