Files
docker-podman-installation-…/scripts/lib/common.sh
T
duffyduckandClaude Opus 5 62ccafbbd1 Anmeldung fehlgeschlagen: sagen, woran es liegt
Die Meldung nannte bisher nur die aufgerufene Adresse. Antwort,
HTTP-Code und curls eigene Fehlermeldung wurden weggeworfen - also
genau das, woran man es erkennt. Uebrig blieb Raten.

Jetzt werden alle drei eingesammelt und ausgewertet:

  HTTP 400/401  Der Proxy Manager hat geantwortet und abgelehnt. Damit
                ist auch gesagt, dass Port, Netz und Proxy stimmen. Die
                Meldung der Schnittstelle steht dabei ("Invalid email or
                password"), dazu der Hinweis auf npm-reset, falls das
                Konto nie angelegt wurde.
  HTTP 000      Gar keine Antwort. Der Reihe nach wird geprueft und
                benannt: NPM_ENABLED=off, Container laeuft nicht, auf
                dem Port lauscht nichts, oder er antwortet nur nicht.
  HTTP 5xx      Es hat etwas geantwortet, aber nicht wie erwartet.

Dazu: ist die Antwort HTML statt JSON, wird das ausdruecklich gesagt und
die erste Zeile gezeigt. Damit ist auf einen Blick erkennbar, dass ein
HTTP-Proxy geantwortet hat und nicht der Proxy Manager - im Test
"<html><head><title>502 Proxy Error</title>...".

Beim Pruefen der Faelle ist ein Fehler aufgefallen: container_running
benutzt RUNTIME ohne Absicherung. War die Runtime nicht erkannt, brach
die Fehlerdiagnose unter "set -u" mit "RUNTIME ist nicht gesetzt" ab -
ausgerechnet die Ausgabe, die helfen soll. Ohne erkannte Runtime lautet
die Antwort jetzt schlicht "nein".

Nebenbei korrigiert: NPM antwortet bei falschen Zugangsdaten mit 400,
nicht mit 401 - der erste Entwurf fing den haeufigsten Fall nicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 23:56:49 +02:00

530 lines
20 KiB
Bash

#!/usr/bin/env bash
# =====================================================================
# common.sh - gemeinsame Funktionen fuer cmk-manage.sh
# Wird per "source" eingebunden, nicht direkt ausgefuehrt.
# =====================================================================
# ---------------------------------------------------------------------
# Ausgabe / Logging
# ---------------------------------------------------------------------
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
C_BLU=$'\033[34m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
else
C_RED=; C_GRN=; C_YEL=; C_BLU=; C_BLD=; C_RST=
fi
log() { printf '%s[ %s ]%s %s\n' "$C_BLU" "INFO" "$C_RST" "$*"; }
ok() { printf '%s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
warn() { printf '%s[ WARN ]%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; }
err() { printf '%s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$*" >&2; }
die() { err "$*"; exit 1; }
debug() { [ -n "${VERBOSE:-}" ] && printf ' > %s\n' "$*" >&2 || true; }
# ---------------------------------------------------------------------
# .env laden
# ---------------------------------------------------------------------
load_env() {
local envfile="${1:-$PROJECT_DIR/.env}"
if [ -f "$envfile" ]; then
set -a
# shellcheck disable=SC1090
. "$envfile"
set +a
ENV_FILE="$envfile"
else
warn "Keine .env gefunden ($envfile) - es gelten die Standardwerte."
ENV_FILE=""
fi
# Rueckfallwerte, falls ein Schluessel in der .env fehlt.
# Die .env bleibt die massgebliche Konfiguration - hier steht nur,
# was gilt, wenn dort nichts eingetragen ist.
CMK_CONTAINER_NAME="${CMK_CONTAINER_NAME:-checkmk}"
NPM_CONTAINER_NAME="${NPM_CONTAINER_NAME:-nginx-proxy-manager}"
CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
CMK_IMAGE="${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}"
NPM_ENABLED="${NPM_ENABLED:-on}"
BACKUP_AUTO="${BACKUP_AUTO:-off}"
CMK_IMAGE_SOURCE="${CMK_IMAGE_SOURCE:-auto}"
PROXY_FORWARD_HOST="${PROXY_FORWARD_HOST:-127.0.0.1}"
PROXY_FORWARD_PORT="${PROXY_FORWARD_PORT:-5000}"
NPM_ADMIN_PORT="${NPM_ADMIN_PORT:-81}"
# Leer = automatisch aus dem Admin-Port ableiten
NPM_ADMIN_URL="${NPM_ADMIN_URL:-http://127.0.0.1:$NPM_ADMIN_PORT}"
BACKUP_DIR="$(abs_path "${BACKUP_DIR:-$PROJECT_DIR/backups}")"
IMAGE_DIR="$(abs_path "${IMAGE_DIR:-$PROJECT_DIR/images}")"
}
# relativen Pfad (./foo) am Projektverzeichnis ausrichten
abs_path() {
case "$1" in
/*) printf '%s\n' "$1" ;;
*) printf '%s\n' "$PROJECT_DIR/${1#./}" ;;
esac
}
# .env-Wert dauerhaft setzen
# env_set KEY WERT [allow_empty]
env_set() {
local key="$1" val="$2" allow_empty="${3:-}" f="${ENV_FILE:-$PROJECT_DIR/.env}"
[ -f "$f" ] || die "Keine .env vorhanden - kann '$key' nicht setzen."
# mehrzeilige Werte wuerden die .env zerstoeren
case "$val" in
*$'\n'*) die "Ungueltiger Wert fuer '$key' (mehrzeilig)." ;;
esac
[ -n "$val" ] || [ -n "$allow_empty" ] || die "Leerer Wert fuer '$key' - abgebrochen."
# Werte mit Leerzeichen gehoeren in der Datei in Anfuehrungszeichen -
# in der Umgebung aber ohne, sonst stecken die Zeichen spaeter im Wert
local quoted="$val"
case "$val" in
*" "*) case "$val" in \"*\") : ;; *) quoted="\"$val\"" ;; esac ;;
esac
if grep -qE "^[#[:space:]]*${key}=" "$f"; then
sed -i -E "s|^[#[:space:]]*${key}=.*|${key}=${quoted}|" "$f"
else
printf '%s=%s\n' "$key" "$quoted" >>"$f"
fi
export "$key=$val"
}
# ---------------------------------------------------------------------
# Container-Runtime erkennen (docker / podman)
# ---------------------------------------------------------------------
detect_runtime() {
if [ -n "${CMK_RUNTIME:-}" ] && command -v "$CMK_RUNTIME" >/dev/null 2>&1; then
RUNTIME="$CMK_RUNTIME"
elif [ -n "${CMK_RUNTIME:-}" ]; then
# Falsch gesetzter Wert darf das Werkzeug nicht unbenutzbar machen
warn "CMK_RUNTIME='$CMK_RUNTIME' ist nicht installiert - suche selbst.
Korrigieren mit: cmk-manage.sh config set CMK_RUNTIME auto"
CMK_RUNTIME=""
detect_runtime
return
elif command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
RUNTIME=docker
elif command -v podman >/dev/null 2>&1; then
RUNTIME=podman
elif command -v docker >/dev/null 2>&1; then
RUNTIME=docker # vorhanden, aber Daemon evtl. gestoppt
else
die "Weder docker noch podman gefunden. Bitte zuerst ./install.sh ausfuehren."
fi
debug "Runtime: $RUNTIME"
}
# Compose-Kommando ermitteln (als Array COMPOSE_CMD)
detect_compose() {
COMPOSE_CMD=()
if [ "$RUNTIME" = docker ]; then
if docker compose version >/dev/null 2>&1; then
COMPOSE_CMD=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE_CMD=(docker-compose)
fi
else
if command -v podman-compose >/dev/null 2>&1; then
COMPOSE_CMD=(podman-compose)
elif podman compose version >/dev/null 2>&1; then
COMPOSE_CMD=(podman compose)
fi
fi
[ ${#COMPOSE_CMD[@]} -gt 0 ] || die "Kein Compose-Kommando gefunden (docker compose / podman-compose)."
debug "Compose: ${COMPOSE_CMD[*]}"
}
compose() {
detect_compose
( cd "$PROJECT_DIR" && "${COMPOSE_CMD[@]}" "$@" )
}
# ---------------------------------------------------------------------
# Container-Helfer
# ---------------------------------------------------------------------
# Ist der NGINX Proxy Manager eingeschaltet? (on/true/1/yes = ja)
npm_enabled() {
case "$(printf '%s' "${NPM_ENABLED:-on}" | tr 'A-Z' 'a-z')" in
on|true|1|yes|ja|y) return 0 ;;
*) return 1 ;;
esac
}
# Ohne erkannte Runtime laesst sich nichts sagen - dann schlicht "nein"
# statt unter "set -u" mitten in einer Fehlerdiagnose abzubrechen.
container_exists() { [ -n "${RUNTIME:-}" ] && $RUNTIME container inspect "$1" >/dev/null 2>&1; }
container_running() {
[ -n "${RUNTIME:-}" ] || return 1
[ "$($RUNTIME container inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]
}
require_cmk_running() {
container_exists "$CMK_CONTAINER_NAME" \
|| die "Container '$CMK_CONTAINER_NAME' existiert nicht. Zuerst: $0 up"
container_running "$CMK_CONTAINER_NAME" \
|| die "Container '$CMK_CONTAINER_NAME' laeuft nicht. Zuerst: $0 up"
}
# Kommando im Checkmk-Container als root ausfuehren
ct() { $RUNTIME exec "$CMK_CONTAINER_NAME" "$@"; }
# ... mit stdin
cti() { $RUNTIME exec -i "$CMK_CONTAINER_NAME" "$@"; }
# ---------------------------------------------------------------------
# OMD-Helfer (im Container)
# ---------------------------------------------------------------------
cmk_sites() {
ct sh -c 'ls -1 /omd/sites 2>/dev/null' | sed '/^$/d' || true
}
cmk_site_exists() {
( set +o pipefail; cmk_sites | grep -qxF "$1" )
}
cmk_site_version() {
ct omd version -b "$1" 2>/dev/null | tr -d '\r' || true
}
# 0 = laeuft komplett, 1 = teilweise, 2 = gestoppt
cmk_site_state() {
local rc=0
ct omd status --bare "$1" >/dev/null 2>&1 || rc=$?
echo "$rc"
}
cmk_site_state_text() {
case "$(cmk_site_state "$1")" in
0) echo "laeuft" ;;
1) echo "teilweise" ;;
*) echo "gestoppt" ;;
esac
}
cmk_versions_installed() {
ct omd versions -b 2>/dev/null | tr -d '\r' || ct sh -c 'ls -1 /omd/versions | grep -v "^default$"' 2>/dev/null || true
}
# ---------------------------------------------------------------------
# Diverses
# ---------------------------------------------------------------------
human_size() {
local b="${1:-0}"
if [ "$b" -ge 1073741824 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f GiB", b/1073741824}'
elif [ "$b" -ge 1048576 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f MiB", b/1048576}'
elif [ "$b" -ge 1024 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f KiB", b/1024}'
else echo "${b} B"; fi
}
file_size() { stat -c %s "$1" 2>/dev/null || echo 0; }
confirm() {
[ -n "${ASSUME_YES:-}" ] && return 0
local prompt="${1:-Fortfahren?} [j/N] " a
read -r -p "$prompt" a </dev/tty || return 1
case "$a" in [jJyY]*) return 0 ;; *) return 1 ;; esac
}
primary_ip() {
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1);exit}}' \
|| hostname -I 2>/dev/null | awk '{print $1}'
}
# =====================================================================
# TUI-Helfer (dialog bevorzugt, sonst whiptail) - ncurses wie im mc
# =====================================================================
UI_BACKTITLE="Checkmk Stack Manager - Container-Runtime: ${RUNTIME:-?}"
# Groesse des Terminals ermitteln (mit Rueckfallwerten)
ui_term_size() {
UI_ROWS="$(tput lines 2>/dev/null || true)"
UI_COLS="$(tput cols 2>/dev/null || true)"
case "$UI_ROWS" in ''|*[!0-9]*) UI_ROWS=24 ;; esac
case "$UI_COLS" in ''|*[!0-9]*) UI_COLS=80 ;; esac
if [ "$UI_ROWS" -lt 10 ]; then UI_ROWS=24; fi
if [ "$UI_COLS" -lt 40 ]; then UI_COLS=80; fi
return 0
}
# Fenstergroesse aus Textlaenge, Anzahl der Eintraege und Terminalgroesse
# berechnen. Vorher waren die Werte fest - bei langem Text blieb dann nur
# eine Zeile fuer die Liste uebrig.
#
# ACHTUNG: hier bewusst mit if-Bloecken statt "[ ... ] && VAR=...".
# Letzteres liefert 1, wenn die Bedingung nicht zutrifft, und beendet
# unter "set -e" die Funktion bzw. das ganze Skript.
#
# ui_calc TEXT FELDER_JE_EINTRAG EINTRAEGE...
ui_calc() {
local text="$1" per="$2"; shift 2
ui_term_size
local tl maxw
tl="$(printf '%s\n' "$text" | wc -l)"
maxw="$(printf '%s\n' "$text" | awk '{ if (length($0) > m) m = length($0) } END { print m+0 }')"
# breiteste Zeile der Liste (Kennung und Beschriftung stehen nebeneinander)
local -a args=("$@")
local i=0 w itemw=0 n=0
while [ "$i" -lt "${#args[@]}" ]; do
w=$(( ${#args[$i]} + 4 ))
if [ $((i+1)) -lt "${#args[@]}" ]; then
w=$(( w + ${#args[$((i+1))]} ))
fi
if [ "$w" -gt "$itemw" ]; then itemw="$w"; fi
i=$(( i + per )); n=$(( n + 1 ))
done
# Breite: der breitere von Text und Liste, plus Rahmen
if [ "$maxw" -gt "$itemw" ]; then UI_W=$(( maxw + 10 )); else UI_W=$(( itemw + 10 )); fi
if [ "$UI_W" -lt 60 ]; then UI_W=60; fi
if [ "$UI_W" -gt $(( UI_COLS - 4 )) ]; then UI_W=$(( UI_COLS - 4 )); fi
# Listenhoehe: moeglichst alle Eintraege zeigen, ohne aus dem Bild zu laufen
local avail=$(( UI_ROWS - tl - 9 ))
if [ "$avail" -lt 3 ]; then avail=3; fi
if [ "$n" -lt "$avail" ]; then UI_MH="$n"; else UI_MH="$avail"; fi
if [ "$UI_MH" -lt 1 ]; then UI_MH=1; fi
UI_H=$(( tl + UI_MH + 8 ))
if [ "$UI_H" -gt $(( UI_ROWS - 2 )) ]; then UI_H=$(( UI_ROWS - 2 )); fi
if [ "$UI_H" -lt 10 ]; then UI_H=10; fi
return 0
}
# Hoehe fuer Fenster ohne Liste (Meldung, Abfrage, Eingabe)
ui_calc_text() { # text zusatzzeilen
local text="$1" extra="${2:-7}"
ui_term_size
local tl maxw
tl="$(printf '%s\n' "$text" | wc -l)"
maxw="$(printf '%s\n' "$text" | awk '{ if (length($0) > m) m = length($0) } END { print m+0 }')"
UI_W=$(( maxw + 10 ))
if [ "$UI_W" -lt 60 ]; then UI_W=60; fi
if [ "$UI_W" -gt $(( UI_COLS - 4 )) ]; then UI_W=$(( UI_COLS - 4 )); fi
UI_H=$(( tl + extra ))
if [ "$UI_H" -gt $(( UI_ROWS - 2 )) ]; then UI_H=$(( UI_ROWS - 2 )); fi
if [ "$UI_H" -lt 8 ]; then UI_H=8; fi
return 0
}
# ---------------------------------------------------------------------
# Rahmenzeichen in PuTTY & Co.
#
# Ohne UTF-8-Locale zeichnen dialog/whiptail die Rahmen ueber die alte
# VT100-Umschaltung "ESC(0". Terminals, die im UTF-8-Modus laufen -
# PuTTY in der Vorgabe -, ignorieren diese Umschaltung und zeigen
# stattdessen die rohen Buchstaben q, x, l, m.
#
# Nachgemessen: bei LC_ALL=C schickt whiptail eine ESC(0-Umschaltung und
# 91 rohe "q"; mit UTF-8-Locale keine einzige. Es genuegt, LC_CTYPE zu
# setzen - Sortierung und Zahlenformat bleiben damit unberuehrt.
# Zwingt ncurses zu echten Unicode-Rahmenzeichen.
#
# Auch mit UTF-8-Locale benutzt ncurses die VT100-Umschaltung ESC(0,
# solange das terminfo sie anbietet (bei TERM=xterm der Fall). Terminals
# im UTF-8-Modus - PuTTY - machen die nicht mit und zeigen q, x, l, m.
# Gemessen mit dialog unter LANG=C.UTF-8, TERM=xterm:
# ohne Schalter : 38 ESC(0, 0 Rahmenbytes, 182 rohe q
# mit Schalter : 0 ESC(0, 220 Rahmenbytes, 0 rohe q
# whiptail (slang) braucht ihn nicht, es zeichnet bei UTF-8-Locale schon
# von sich aus Unicode.
ui_use_unicode_acs() {
# CMK_TUI_ACS=1 erzwingt umgekehrt das alte Verhalten - fuer
# Terminals, die ESC(0 koennen, aber kein UTF-8.
if [ -n "${CMK_TUI_ACS:-}" ]; then
unset NCURSES_NO_UTF8_ACS 2>/dev/null || true
return 0
fi
export NCURSES_NO_UTF8_ACS=1
return 0
}
ui_fix_locale() {
[ -n "${UI_LOCALE_DONE:-}" ] && return 0
UI_LOCALE_DONE=1
UI_LOCALE_NOTE=""
case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
*UTF-8*|*utf-8*|*UTF8*|*utf8*) ui_use_unicode_acs; return 0 ;;
esac
local l
for l in C.UTF-8 C.utf8 en_US.UTF-8 en_US.utf8 de_DE.UTF-8 de_DE.utf8; do
if ( set +o pipefail; locale -a 2>/dev/null | grep -qix "$l" ); then
# LC_ALL sticht jede einzelne Kategorie aus - solange es auf
# einem Nicht-UTF-8-Wert steht, bliebe LC_CTYPE wirkungslos.
if [ -n "${LC_ALL:-}" ]; then unset LC_ALL; fi
export LC_CTYPE="$l"
UI_LOCALE_NOTE="LC_CTYPE auf $l gesetzt (war nicht UTF-8)"
ui_use_unicode_acs
return 0
fi
done
# Keine UTF-8-Locale vorhanden - dann bleibt nur ASCII
UI_ASCII=1
UI_LOCALE_NOTE="keine UTF-8-Locale gefunden - einfache Rahmen"
return 1
}
# Zusaetzliche Argumente, wenn nur ASCII-Rahmen moeglich sind
ui_ascii_args() {
UI_EXTRA=()
case "${CMK_TUI_ASCII:-}${UI_ASCII:-}" in
'') return 0 ;;
esac
if [ "${UI_BIN:-}" = dialog ] && dialog --ascii-lines --print-version >/dev/null 2>&1; then
UI_EXTRA=(--ascii-lines)
elif [ -z "${UI_ASCII_WARNED:-}" ]; then
UI_ASCII_WARNED=1
warn "Einfache Rahmen kann nur 'dialog' zeichnen - 'whiptail' kennt
die Option nicht. Installieren mit:
dnf install -y dialog (RHEL/Rocky/Alma)
apt-get install -y dialog (Debian/Ubuntu)"
fi
return 0
}
ui_init() {
ui_fix_locale || true
# Geometrie IMMER setzen - sonst laeuft das Skript unter "set -u" auf
# eine unbound variable, wenn UI_BIN von aussen vorgegeben wurde.
UI_H="${UI_H:-22}"; UI_W="${UI_W:-78}"; UI_MH="${UI_MH:-13}"
if [ -z "${UI_BIN:-}" ]; then
if command -v dialog >/dev/null 2>&1; then UI_BIN=dialog
elif command -v whiptail >/dev/null 2>&1; then UI_BIN=whiptail
else UI_BIN=""; fi
export DIALOGRC="${DIALOGRC:-}"
fi
# Einfache Rahmen koennen nur mit dialog gezeichnet werden. Ist das
# gewuenscht und dialog vorhanden, wird es dafuer vorgezogen.
if [ -n "${CMK_TUI_ASCII:-}${UI_ASCII:-}" ] && command -v dialog >/dev/null 2>&1; then
UI_BIN=dialog
fi
ui_ascii_args
}
ui_available() { ui_init; [ -n "$UI_BIN" ]; }
ui_require() {
ui_available || die "Fuer die TUI wird 'dialog' oder 'whiptail' benoetigt.
Debian/Ubuntu : apt-get install -y dialog
RHEL/Rocky : dnf install -y dialog"
UI_BACKTITLE="Checkmk Stack Manager - Runtime: $RUNTIME"
}
# =====================================================================
# Alle Dialoge laufen ueber genau drei Engstellen.
#
# Damit stimmt die Fenstergroesse automatisch - auch fuer Dialoge, die
# spaeter dazukommen. NEUE FENSTER BITTE IMMER UEBER DIESE DREI BAUEN
# und niemals "$UI_BIN" direkt aufrufen; "cmk-manage.sh selftest"
# prueft das nach.
#
# _ui_list Listenfenster (menu, checklist, radiolist) -> ui_calc
# _ui_box Textfenster (msgbox, yesno, inputbox, ...) -> ui_calc_text
# _ui_file Dateianzeige (textbox) -> volles Bild
# =====================================================================
# _ui_list WIDGET TITEL TEXT FELDER_JE_EINTRAG EINTRAEGE...
_ui_list() {
ui_init
local widget="$1" title="$2" text="$3" per="$4"; shift 4
ui_calc "$text" "$per" "$@"
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
"$widget" "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
}
# _ui_box WIDGET TITEL TEXT ZUSATZZEILEN [weitere Argumente]
# Widgets ohne Rueckgabe (msgbox, yesno, infobox) liefern nur den Status.
_ui_box() {
ui_init
local widget="$1" title="$2" text="$3" extra="$4"; shift 4
ui_calc_text "$text" "$extra"
case "$widget" in
--inputbox|--passwordbox)
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
"$widget" "$text" "$UI_H" "$UI_W" "$@" 3>&1 1>&2 2>&3 ;;
*)
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
"$widget" "$text" "$UI_H" "$UI_W" "$@" ;;
esac
}
# _ui_file TITEL DATEI - nutzt das Terminal so weit wie moeglich aus
_ui_file() {
ui_init
ui_term_size
# Farbige Ausgaben landen hier als rohe Steuerzeichen (^[[1m ...),
# weil dialog/whiptail sie nicht auswerten. Die Farbvariablen werden
# beim Laden dieser Datei einmal festgelegt - ein NO_COLOR beim
# spaeteren Aufruf wirkt also nicht mehr. Deshalb hier entfernen;
# das greift fuer JEDE Textanzeige.
local clean; clean="$(mktemp)"
sed -E 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\x1b\([AB012]//g' "$2" >"$clean" 2>/dev/null \
|| cp "$2" "$clean"
set -- "$1" "$clean"
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$1" \
--textbox "$2" $(( UI_ROWS - 2 )) $(( UI_COLS - 4 ))
local rc=$?
rm -f "$clean"
return $rc
}
# ---------------------------------------------------------------------
# Die eigentlichen Dialoge - alle nur noch duenne Huellen
# ---------------------------------------------------------------------
ui_menu() { local t="$1" x="$2"; shift 2; _ui_list --menu "$t" "$x" 2 "$@"; }
ui_checklist() { local t="$1" x="$2"; shift 2; _ui_list --checklist "$t" "$x" 3 "$@"; }
ui_radiolist() { local t="$1" x="$2"; shift 2; _ui_list --radiolist "$t" "$x" 3 "$@"; }
ui_input() { _ui_box --inputbox "$1" "$2" 10 "${3:-}"; }
ui_password() { _ui_box --passwordbox "$1" "$2" 10; }
ui_msg() { _ui_box --msgbox "$1" "$2" 7; }
ui_yesno() { _ui_box --yesno "$1" "$2" 8; }
ui_info() { _ui_box --infobox "$1" "$2" 5; }
ui_text() { _ui_file "$1" "$2"; }
# Kommando ausfuehren, Ausgabe live in Datei, danach in Textbox anzeigen
ui_run() { # title cmd...
ui_init
local title="$1"; shift
local logf; logf="$(mktemp)"
ui_info "$title" "Bitte warten ...
$*"
if "$@" >"$logf" 2>&1; then
ui_text "$title - erfolgreich" "$logf"
rm -f "$logf"; return 0
else
ui_text "$title - FEHLGESCHLAGEN" "$logf"
rm -f "$logf"; return 1
fi
}
# ---------------------------------------------------------------------
# Dateiauswahl im Midnight-Commander-Stil (funktioniert auch mit whiptail)
# ---------------------------------------------------------------------
ui_pick_file() { # startdir [glob] -> Dateipfad auf stdout
ui_init
local dir="${1:-$PWD}" glob="${2:-*}" sel
dir="$(cd "$dir" 2>/dev/null && pwd || echo "$PWD")"
while :; do
local -a items=()
items+=(".." "<Verzeichnis hoeher>")
local d f
while IFS= read -r d; do
[ -n "$d" ] && items+=("$d/" "<DIR>")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort)
while IFS= read -r f; do
[ -n "$f" ] || continue
items+=("$f" "$(human_size "$(file_size "$dir/$f")")")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -name "$glob" -printf '%f\n' 2>/dev/null | sort)
sel="$(ui_menu "Datei waehlen" "Verzeichnis: $dir\nFilter: $glob" "${items[@]}")" || return 1
case "$sel" in
"..") dir="$(dirname "$dir")" ;;
*/) dir="$dir/${sel%/}" ;;
*) printf '%s\n' "$dir/$sel"; return 0 ;;
esac
done
}