#!/usr/bin/env bash
#!/usr/local/bin/bash
#!/bin/bash
#!/bin/bash -u
#!/usr/local/bin/bash -u
#-------------------------------------------------------------------------------
# bashcode.sh                               (C) 2004-2026 T.Birnthaler OSTC GmbH
# Anweisungen der "bash/ksh/sh" ausprobieren.
# $Id: bashcode.sh,v 1.11 2015/05/08 09:22:57 tsbirn Exp tsbirn $
#-------------------------------------------------------------------------------
# Shee-Bang-Zeile "#!/bin/bash -u" IMMER in 1.Zeile/1.Spalte angeben
# (-u=undefined --> Zugriff auf nicht definierte Variable loest Fehler aus).
#-------------------------------------------------------------------------------
# Aufgrund des hohen Alters der Shell und ihrer Verwendung als interaktiver
# Kommando-Interpreter sind einige ihrer Syntax-Eigenschaften widerspruechlich.
#
# Dies betrifft vor allem LEERZEICHEN, die an manchen Stellen vorhanden sein
# MUESSEN, an anderen Stellen aber NICHT erlaubt sind. Ist verwirrend, da in
# den ueblichen Programmiersprachen solche Regeln (meist) nicht existieren.
#
# Ebenso koennen in Texten, Pfadnamen und Variablenwerten Zeichen vorkommen,
# die fuer die Shell eine Sonderbedeutung haben. In einem solchen Fall muessen
# sie per \, "..." oder '...' geschuetzt werden. Shellsonderzeichen sind (26!):
#
#           +--------------------------------------------------+
#           |   *   ?   [   ]   =   $   <   >   |   &   \      |
#           |   "   '   `   ;   (   )   {   }   ~   ^   !   #  |
#           |          <Space>   <Tabulator>   <Return>        |
#           +--------------------------------------------------+
#
#-------------------------------------------------------------------------------
# INHALTSVERZEICHNIS
# ==================
# 0) Dokumentation
# 1) Shell-Variablen initialisieren
# 1a) Sinnvolle Variablen-Initialisierungen am Programmanfang
# 1b) Bash-interne Variablen
# 2) Umgebungs-Variablen erzeugen und initialisieren
# 3) Shell/Umgebungs-Variable loeschen
# 4) Signale abfangen
# 5) Eingabe in Variable einlesen und ausgeben
# 5b) Ausgabe (Escape-Zeichen, echo, printf)
# 5c) Escape/Backslash-Sequenzen
# 5d) Here-Document
# 5e) Here-String
# 5f) Here-Document/String + Kommando-Substitution
# 5g) Escape-String
# 5h) Lokalisierter String
# 6) Kommando-Substitution (Befehlsausgabe in anderem Kmdo weiterverwenden)
# 7) Prozess-Substitution (Kmdo-Eingabe aus temporaerer Datei holen)
# 7b) Datei-Substitution
# 8) Bedingungen ueberpruefen
# 9) Weitere Schreibweise fuer Bedingungs-Test
# 10) Regulaere Ausdruecke vergleichen
# 11) Rechnen mit Ganzzahlen (per "expr" immer moeglich, ohne nur in bash + ksh)
# 12) Rechenoperatoren bei ((...)) (bzw. let; Arithmetischer Ausdruck)
# 13) Rechnen mit Fliesskommazahlen ("bc" oder "dc")
# 14) Logische Operatoren bei [[...]] (Bedingter Ausdruck)
# 14b) Moegliche Operatoren in [[...]]
# 15) if-then-elif-else-Verzweigung
# 16) for-Schleife (Wort-Liste abarbeiten)
# 17) for-Schleife (Aufruf-Argumente abarbeiten)
# 18) while-do-Schleife
# 19) until-do-Schleife
# 20) repeat-until-Schleife GIBT ES NICHT!
# 21) Einfache Schleife vorzeitig neu starten bzw. abbrechen
# 22) Verschachtelte Schleife vorzeitig neu starten bzw. abbrechen
# 23) Zaehlschleife
# 24) Interaktive Auswahl aus einer Liste
# 25) case-Mehrfachverzweigung
# 26) Label und "goto" SIND NICHT MOEGLICH!
# 27) Funktions-Definition ohne Parameter
# 28) Funktions-Aufruf OHNE Uebergabe von Argumenten
# 29) Funktions-Definition mit Nutzung von Positions-Parametern $1 $2 $3 ...
# 30) Funktions-Aufruf MIT Uebergabe von Argumenten
# 31) Skript-Aufruf-Argumente/Parameter verarbeiten
# 31a) Usage-Meldung (evtl. wird eine Fehler-Meldung in "$1" uebergeben)
# 31b) Je nachdem, ob Skript-Parameter angegeben oder nicht, etwas anderes tun
# 31c) Aufruf-Optionen verarbeiten
# 31d) Skript autom. mit "root"-Rechten neu aufrufen (oder abbrechen) falls nicht so:
# 32) Weitere spezielle Skript-Variablen
# 33) Bedingte Variablen
# 34) Variablen-Manipulationen (String-Operationen)
# 35) Verzweigung abhaengig vom Skript-Namen
# 36) Indiziertes Array
# 37) Assoziatives Array
# 38) Temporaere(s) Datei/Verz. anlegen + am Skriptende automatisch loeschen
# 39) Betriebssystem-Abfrage
# 40) Tipps
#-------------------------------------------------------------------------------
# TODO:
# * Fliesskomma-Rechnung mit Shell-Variablen per bc, dc, awk, ... und | <<< <<
# * Here-Dokument an Kommando pipen
# * Prozess-Substition: von temp. Datei lesen: ... < <(...) oder cat <(...) | ...
# * Alle Arrayelemente (Index/Schluessel, Wert) in einer Schleife abarbeiten
# * Shell-Optionen: Unterschied "shopt -p" und "set -o"
# * Shell-Optionen: Vollstaendige Liste
# * 14) Logische Operatoren bei [[...]] (Bedingter Ausdruck)
# * 14b) Moegliche Operatoren in [[...]]
# * VAR=$((FORMEL))     # Berechnen + Ergebnis auf stdout zurueckgeben (let-Ersatz)
#                                   + Exit-Status 0 bei Erg. != 0, 1 bei Erg. != 0
# * VAR=$[FORMEL]       # Berechnen + Exit-Status 0/1 zurueckgeben
# * VAR=$[[BEDINGUNG]]  # Auswerten + Exit-Status 0/1 zurueckgeben
# * Spezialvariable $_
# * Bash-Version abfragen: BASH_VERSINFO, BASH_VERSION
# * Bash-Variable intern: BASH_...
# * Standard-Shell-Variablen und Environment-Variablen:
#   + PATH, HOME, TERM, SHELL, USER, IFS
#   + TMP, TMPDIR, TMPFILE
#   + LANG, LANGUAGE, LC_ALL, LC_*
#   + LS_OPTIONS, ...
#   + EDITOR, VISUAL, ...
# * Rechnen mit Datum- und Zeitwerten (date, UNIX timestamp)
# * Pattern Matching * ? [...] [!...] [^...] ** [:class:] [=c=] [.symbol.]
#   (** *** + shopt globstar)
#   (?(..) *(...) +(...) @(...) !(...) + shopt extglob)
# * Koprozesse: coproc [NAME] CMD [REDIRECTIONS]
# * Unterschiede Bash-Version 3.0 3.1 3.2 4.0 4.1 4.2 4.3 4.4 5.0 ...
#-------------------------------------------------------------------------------
# DONE:
# * Aufruf-Argumente "VON INNEN" setzen: set -- ARGUMENTS
# * Fliesskomma-Rechnung bc/dc-Beispiele
# * Zaehlen per {1..10}
# * do ... until oder do ... while Schleife?  GIBT ES NICHT!
# * Lokalisierter String $"..."
#   $'...' ersetzt Escape-Zeichen \n \r ... durch ANSI-Zeichen
#   $"..." ersetzt Escape-Zeichen \n \r ... durch Zeichen gemaess Locale-Einstellung
#          (bei Locale C oder POSIX ignoriert)
# * Here-Document CMD <<EOF
#                 ...
#                 EOF
# * Here-String: CMD <<< "STRING"
# * Datei-Substitution $(<DATEI) = Dateiinhalt einsetzen
#   z.B. VAR=$(<DATEI) entspricht VAR=$(cat datei)
# * printf FORMAT ARGS...   # Formatierung -v VAR %b %d %i %s %c %f ...
# * Shell-Optionen shopt
# * Rechnen mit "bc" und "dc" (Fliesskomma)
# * Prozess-Substition <(...) >(...): Wie auf temp. Ausgabedatei zugreifen?
# * Variablen-Substitution mit Textersatz (% %% # ## ^ ^^ , ,, / // /# /%)
# * Rechenoperationen nochmal ueberpruefen
# * "autoload" gibt es nur in "ksh"
# * Geht @ statt let? --> NEIN!
# * Umlaute --> aeoeueAeOeUess austauschen (portabel)
# * help --> Liste der Shell-Schluesselworte
# * help CMD --> Kurzerklaerung Shell-Kommando
# * Regulaere Ausdruecke mit [[ ... =~ ... ]]
# * Prompt-String: PS1, PS2, PS3, PS4
#-------------------------------------------------------------------------------

# Variable "status" wegen Option "-u" (undefined) in Shee-Bang-Zeile definieren
status=

#-------------------------------------------------------------------------------
# 0) Dokumentation
#-------------------------------------------------------------------------------
help                                      # Liste aller Bash-Befehle
echo
help cd                                   # Hilfe zu Bash-Befehl "cd" (usw.)
echo
help shopt                                # Hilfe zu Bash-Befehl "shopt"
echo
set -o                                    # Shell-Optionen ausgeben (-o=option)
echo
shopt -p                                  # Shell-Optionen ausgeben (-p=print)

#-------------------------------------------------------------------------------
# 1) Shell-Variablen initialisieren (KEIN Leerzeichen um "=" erlaubt!)
# (auch wenn nicht grundsaetzlich notwendig, immer "..." um zugewiesenen Text!)
#-------------------------------------------------------------------------------
echo
  TXT=                                    # Leerer Wert
  TXT=""                                  # Leerer Wert
  TXT=''                                  # Leerer Wert
 TEXT=EinWort                             # KEINE Quotierung noetig
TEXT2="Mehr als ein Wort"                 # Quotierung notwendig!
 ZAHL="12345"                             # Zahl (als Text repraesentiert!)
 SAVE="$PATH"                             # Suchpfad merken
 PATH=/bin:/usr/bin:/sbin:/usr/sbin       # Standard-Suchpfad setzen
 PATH="/bin:/usr/bin:/sbin:/usr/sbin"     # Analog (Quotierung unnoetig)

#-------------------------------------------------------------------------------
# 1a) Sinnvolle Variablen-Initialisierungen am Programmanfang
#-------------------------------------------------------------------------------
IFS=" 	
"                                    # Standard-Shell-Trennz. (LEERZ, TAB, CR)
PATH="/bin:/usr/bin"                 # Normaler Suchpfad: Programme nur hier suchen
PATH="/bin:/usr/bin:/sbin:/usr/sbin" # root-Suchpfad: Programme nur hier suchen
PATH="$SAVE"                         # Suchpfad wiederherstellen
LS_OPTIONS=                          # "ls"-Optionen aus
GROUP="users"                        # Standard-Gruppe
GROUP="staff"                        # Standard-Gruppe
WORKDIR="$HOME"                      # Arbeitsverzeichnis
EDITOR=                              # Standard-Editor
VISUAL=                              # Standard-Editor
LANG="C"                             # Standard-Sprache (wegen Meldungen!)
LANGUAGE="C"                         # Standard-Sprache (wegen Meldungen!)
LC_ALL="C"                           # Standard-Sprache (wegen Meldungen!)
SHELL="/bin/sh"                      # Standard-Shell
SHELL="/bin/ksh"                     # Standard-Shell
SHELL="/bin/bash"                    # Standard-Shell
LD_LIBRARY_PATH="/lib:/usr/lib"      # Standard-Bibliotheken
DBG=                                 # Debugging aus
TMP="/tmp"                           # Temporaeres Verz. setzen
TMPDIR="/tmp"                        # Temporaeres Verz. setzen

#-------------------------------------------------------------------------------
# 1b) Bash-interne Variablen
# TODO
#-------------------------------------------------------------------------------
sed 's/#.*$//' <<EOF | while read V; do eval "echo $V=\$$V"; done
    BASH                         # Pfadname der Bash-Instanz
    BASHOPTS                     # Aktivierte Bash-Optionen (shopt)
    BASHPID                      # Bash-Prozess-ID (analog $$)
    BASH_ALIASES                 # Array: "alias"-Kommandos
    BASH_ARGC                    # Anzahl Skript/Funktions-Parameter
    BASH_ARGV                    # Skript/Funktions-Parameter
    BASH_ARGV0                   # Skriptname (analog $0, aber writable)
    BASH_COMPAT                  # Array: ge"hash"en-Kommandos
    BASH_CMDS                    # Array: ge"hash"en-Kommandos
    BASH_COMMAND                 # Aktuell ausgefuehrtes Kommando
    BASH_COMPLETION              # 
    BASH_COMPLETION_COMPAT_DIR   #
    BASH_COMPLETION_DIR          #
    BASH_EXECUTION_STRING        # Per -c CMD uebergebenes Kommando
    BASH_ENVS                    # Datei: Umgebung von Bash-Skript aufbauen
    BASH_LINENO                  # Array: Zeilennummern von Fkt.aufrufen
    BASH_LOADABLE_PATH           # Array: Pfadliste mit dyn. Builtins (enable)
    BASH_REMATCH                 # Array: Geklammerte Regex-Matchteile
    BASH_SOURCE                  #
    BASH_SUBSHELL                # Shell-Verschachtelungstiefe (Start: 0)
    BASH_VERSINFO                # Array: 0=major 1=minor 2=patch 3=build 4=releas status
    BASH_VERSION                 # Versions-String
    BASH_XTRACEFD                # File-Diskriptor fuer -x Ausgabe
    CMP_CWORD                    # 
    CMP_KEY                      # 
    CMP_LINE                     # 
    CMP_POINT                    # 
    CMP_TYPE                     # 
    CMP_WORDBREAKS               # 
    CMP_WORDS                    # 
    COPROC                       # 
EOF

#-------------------------------------------------------------------------------
# 2) Umgebungs-Variablen erzeugen und initialisieren (KEIN Leerz. um "=" erlaubt!)
# (auch wenn nicht grundsaetzlich notwendig, immer "..." um zugewiesenen Text!)
#-------------------------------------------------------------------------------
export GLOBAL1                            # Variante A: ok
GLOBAL1="Wert 1"                          # (erst "export", dann Zuweisung)

GLOBAL2="Wert 2"                          # Variante B: ok
export GLOBAL2                            # (erst Zuweisung, dann "export")

export GLOBAL3="Wert 3"                   # Variante C: ok (nur bash + ksh)
                                          # ("export" + Zuweisung gleichzeitig)
#-------------------------------------------------------------------------------
# 3) Shell/Umgebungs-Variable loeschen
#-------------------------------------------------------------------------------
TXT=""                                    # Anschliessend leer aber definiert
unset GLOBAL1                             # Anschliessend leer und undefiniert

#-------------------------------------------------------------------------------
# 4) Signale abfangen (ALLE Signale ausser SIGKILL/9 sind abfangbar!)
# Liste der Signale mit "kill -l" bzw. "man 7 signal"
# 0 = EXIT = Skriptende, DEBUG --> Pro Kommando, RETURN --> Pro Funktionsaufruf
# ERR = Option -e + Skriptende
#-------------------------------------------------------------------------------
trap 'echo "Signal angekommen"' 1 2 3 15  # Signale 1=HUP, 2=INT=Strg-C, 3=QUIT
                                          #        15=TERM=kill abfangen
trap                                      # Liste Signalen zugewiesenes Kommando
trap '' 1 2 3 15                          # Obige 4 Signale ignorieren
trap 1 2 3 15                             # Standardbehandlung obiger 4 Signale

#-------------------------------------------------------------------------------
# 5) Eingabe in Variable einlesen und ausgeben (REPLY ist Standardvar.)
#-------------------------------------------------------------------------------
echo                                      #
echo -n "A) Bitte Text eingeben: "        # -n=KEIN Zeilenvorschub nach "echo"
                                          # (Leerzeichen am Zl.ende sinnvoll)
read EINGABE                              # Benutzereingabe lesen (bis RETURN)
echo "Eingegeben wurde: $EINGABE"         #

echo
read -p "B) Bitte Text eingeben: "        # Analog (-p=prompt) in REPLY einlesen
echo "Eingegeben wurde: $REPLY"           #

WAHL=x                                    # Standardwert
echo                                      #
read -n 1 -t 5 -p "C) Zeichen eingeben: " Z  # -n=number of chars, -t=timeout
echo                                      #
echo "Eingegeben wurde: $Z"               # 1 Zeichen lesen, max. 5 Sek. warten

echo -n "D) Bitte Text eingeben: "        #
read -r VAR                               # raw = keine Escape-Sequenz
echo "VAR=$VAR"                           #
read -i "start" VAR                       # initial = Starttext
echo "VAR=$VAR"                           #
read -s VAR                               # silent = Getippte Z. nicht anzeigen
echo "VAR=$VAR"                           #
read -a ARR                               # array = Worte in Array ab 0 einlesen
echo "ARR($ARR[#])=${ARR[*]}"             #
read -t 2.5 VAR                           # timeout 2.5 Sekunden bis Eingabe
echo "VAR=$VAR"                           #

while read IP HOST REST                   # Datei "/etc/hosts" zeilenweise
do                                        # einlesen in 3 Variablen
	case "$IP" in                         #   Worte durch Leerraum getrennt
		[0-9]*) echo "$IP $HOST ($REST)";;#   IP   = 1. Wort
		*) ;;                             #   HOST = 2. Wort
    esac                                  #   REST = restliche Worte
done < /etc/hosts                         #

SAV="$IFS"                                # Trennzeichen fuer Worte merken
IFS=":"                                   # Trennzeichen fuer Worte = ":"
while read USR PW ID GID CMT HOME SH      # Datei "/etc/passwd" zeilenweise
do                                        # einlesen in 7 Variablen
    echo "$USR $ID $GID $CMT $HOME $SH"   #   Worte durch ":" getrennt
done < /etc/passwd                        #   USR = 1. Wort, PW = 2. Wort, usw.
IFS="$SAV"                                # Trennzeichen wieder auf Std setzen

#-------------------------------------------------------------------------------
# 5b) Ausgabe (Escape-Zeichen, echo, printf)
# echo erzeugt automatisch Zeilenvorschub am Ende (ausser -n oder \c)
# printf erzeugt keinen Zeilenvorschub am Ende (ausser \n im Format)
#-------------------------------------------------------------------------------
echo "Text"                               # Auf Standardausgabe ausgeben
echo "Text" 1>&2                          # Auf Fehlerausgabe ausgeben
echo -e "Text\tText\r\n"                  # -e=Escape-Sequenzen erkennen
echo -E "Text\tText\r\n"                  # -E=Escape-Sequenzen nicht erkennen
echo -n "Kein"                            # -n=Kein Zeilenvorschub (NoNewline)
echo " Vorschub"                          #

ZAHL=12345
TEXT="hallo"
FLOAT="3.14159"

printf "Wert: %d (Prozent)\n" 19          # Auf Standardausgabe ausgeben
printf -v VAR "Wert: %d (Prozent)\n" 19   # Ausgabe in Variable VAR speichern
echo "VAR=$VAR"                           #

# %d/%i = Dezimalzahl, %f = Fliesskommazahl, %s = Text
printf "Zahl: |%d|\n" $ZAHL               # Dezimalzahl formatiert ausgeben
printf "Text: |%s|\n" $TEXT               # Text formatiert ausgeben
printf "Fliesskomma: |%f|\n" $FLOAT       # Fliesskommazahl formatiert ausgeben
printf "|%d %s %f|\n" $ZAHL $TEXT $FLOAT  # 3 Werte formatiert ausgeben

# %d/%i = Dezimalzahl/Integer
printf "Zahl: |%8d|\n" $ZAHL              # Breite 8 (rechtsbuendig)
printf "Zahl: |%-8i|\n" $ZAHL             # Breite 8 (linksbuendig)
printf "Zahl: |%08d|\n" $ZAHL             # Breite 8 (fuehrende Nullen)
printf "Zahl: |%+8i|\n" $ZAHL             # Breite 8 (mit Vorzeichen +)
printf "Zahl: |%2d|\n" $ZAHL              # Breite 2 (gesprengt da breiter)

# %f = Fliesskommazahl
printf "Fliesskomma: |%8f|\n" $FLOAT      # Breite 8 (rechtsbuendig)
printf "Fliesskomma: |%-8f|\n" $FLOAT     # Breite 8 (linksbuendig)
printf "Fliesskomma: |%08f|\n" $FLOAT     # Breite 8 (fuehrende Nullen)
printf "Fliesskomma: |%+8f|\n" $FLOAT     # Breite 8 (mit Vorzeichen +)
printf "Fliesskomma: |%8.0f|\n" $FLOAT    # Breite 8, 0 Nachkommast. (gerundet)
printf "Fliesskomma: |%8.1f|\n" $FLOAT    # Breite 8, 1 Nachkommast. (gerundet)
printf "Fliesskomma: |%8.2f|\n" $FLOAT    # Breite 8, 2 Nachkommast. (gerundet)
printf "Fliesskomma: |%8.3f|\n" $FLOAT    # Breite 8, 3 Nachkommast. (gerundet)
printf "Fliesskomma: |%8.4f|\n" $FLOAT    # Breite 8, 4 Nachkommast. (gerundet)
printf "Fliesskomma: |%8.5f|\n" $FLOAT    # Breite 8, 5 Nachkommast. (gerundet)
printf "Fliesskomma: |%8.6f|\n" $FLOAT    # Breite 8, 6 Nachkommast. (gerundet)
printf "Fliesskomma: |%2f|\n" $FLOAT      # Breite 2 wird gesprengt da breiter

# %s = String
printf "Text: |%8s|\n" $TEXT              # Breite 8 (rechtsbuendig)
printf "Text: |%-8s|\n" $TEXT             # Breite 8 (linksbuendig)
printf "Text: |%2s|\n" $TEXT              # Breite 2 (gesprengt da breiter)
printf "Text: |%2.2s|\n" $TEXT            # Breite 2 (abgeschnitten)

# %c = Einzelzeichen (character, 1. Zeichen)
printf "Zeichen: |%c%c%c|\n" hallo 123 text

# %b = Backslash/Escape-Sequenz interpretieren
ESC='abc\bdef\tghi\n'
printf "Escape: |%s| |%b|\n" "$ESC" "$ESC"

# %q = Quotieren fuer Shell
printf "Quote: |%s| |%q|\n" "$ESC" "$ESC"

# %(fmt)T = Datum+Zeitformat (Time)
printf "Zeit: |%(%d.%m.%Y %T)T|\n"

#-------------------------------------------------------------------------------
# 5c) Escape/Backslash-Sequenzen
#-------------------------------------------------------------------------------
echo -e "abc\adef"                        # Ton (alert)
echo -e "abc\bdef"                        # Rueckschritt (backspace)
echo -e "abc\cdef"                        # Weitere Ausgabe unterdruecken (continue)
echo -e "abc\edef"                        # ESCAPE-Zeichen
echo -e "abc\Edef"                        # ESCAPE-Zeichen
echo -e "abc\fdef"                        # Neue Seite (form feed)
echo -e "abc\ndef"                        # Neue Zeile (newline)
echo -e "abc\rdef"                        # Wagenruecklauf (carriage retun)
echo -e "abc\tdef"                        # Tabulator (horizontal)
echo -e "abc\vdef"                        # Tabulator (vertical)
echo -e "abc\\def"                        # Backslash
echo -e "abc\0140def"                     # Octalcode
echo -e "abc\x60def"                      # Hexadezimalcode

#-------------------------------------------------------------------------------
# 5d) Here-Document
# Per Umlenkung "<<" Dokument im Skript von stdin lesen,
# Variablen-Ersetzung + Kommando-Substitution findet statt
#-------------------------------------------------------------------------------
cat <<EOF
usage: $(basename $0) [OPT...] [FILE...]
error: $msg
       -a    Alle
       -v    Verbose
       -h    Helb
EOF

# Einrueckung per Tabulator am Zeilenanfang ignorieren
cat <<-EOF
	usage: $(basename $0) [OPT...] [FILE...]
	error: $msg
	       -a    Alle
	       -v    Verbose
	       -h    Helb
EOF

# Mit "..." quotieren --> $VAR und $(CMD) nicht mehr ersetzen
cat <<"EOF"
usage: $(basename $0) [OPT...] [FILE...]
error: $msg
       -a    Alle
       -v    Verbose
       -h    Helb
       !!
EOF

# Mit '...' quotieren --> $VAR und $(CMD) nicht mehr ersetzen
cat <<'EOF'
usage: $(basename $0) [OPT...] [FILE...]
error: $msg
       -a    Alle
       -v    Verbose
       -h    Helb
       !!
EOF

#-------------------------------------------------------------------------------
# 5e) Here-String
# Per Umlenkung "<<<" String im Skript von stdin lesen,
# Variablen-Ersetzung + Kommando-Substitution findet statt
#-------------------------------------------------------------------------------
sort <<< $'hallo\nwelt\nmir\ngeht\nes\ngut'

read VAR <<< "text"
echo $VAR
read VAR <<< 'text'
echo $VAR
read VAR <<< text
echo $VAR

#-------------------------------------------------------------------------------
# 5f) Here-Document/String + Kommando-Substitution
# Mehrere Shell-Variablen aus stdout eines Kommandos fuellen
#-------------------------------------------------------------------------------
read HOUR MINUTE SECOND <<EOF
$(date +"%H %M %S")
EOF
echo $HOUR $MINUTE $SECOND

read HOUR MINUTE SECOND <<< $(date +"%H %M %S")
echo $HOUR $MINUTE $SECOND

#-------------------------------------------------------------------------------
# 5g) Escape-String
# $'...' konvertiert Escape-Sequenzen in entsprechende Steuerzeichen
#-------------------------------------------------------------------------------
echo -n "$IFS" | od -c                  # IFS-Inhalt ausgeben
IFS=$' \t\n'                            # IFS auf Standardwert setzen
echo -n "$IFS" | od -c                  # IFS-Inhalt ausgeben

echo $'Text\tText\r\n'                  # Escape-Sequenzen vor Ausgabe umwandeln
echo -e "Text\tText\r\n"                # -e=Escape-Sequenzen erkennen
echo -E $'Text\tText\r\n'               # Escape-Sequenzen vor Ausgabe umwandeln
echo -E "Text\tText\r\n"                # Escape-Sequenzen NICHT erkennen

#-------------------------------------------------------------------------------
# 5h) Lokalisierter String
# Message catalog abhaengig von Variablen "LC_MESSAGE" bzw. "TEXTDOMAIN" +
# "TEXTDOMAINDIR" --> TEXTDOMAINDIR/de/LC_MESSAGES/TEXTDOMAIN.mo.
# TODO
#-------------------------------------------------------------------------------
TEXTDOMAINDIR="."
TEXTDOMAIN="bashcode.sh"

 DIR="$TEXTDOMAIN/de/LC_MESSAGES"
FILE="$TEXTDOMAIN/de/LC_MESSAGES/$TEXTDOMAIN.po"

mkdir -p $DIR
echo 'msgid "Hello world"' > $FILE
echo 'msgstr "Hallo welt"' >> $FILE

cd $DIR
msgfmt -o $TEXTDOMAIN.mo $TEXTDOMAIN.po
ls -l
file *
cd -

LC_MESSAGES=C  echo $"hello world" "(LC_MESSAGES=$LC_MESSAGES)"
LC_MESSAGES=de echo $"hello world" "(LC_MESSAGES=$LC_MESSAGES)"

#-------------------------------------------------------------------------------
# 6) Kommando-Substitution
# Kmdo-Standard-Ausgabe in anderem Kmdo verwenden
# * Alte Syntax `...`  = in jeder (Bourne)Shell erlaubt
# * Neue Syntax $(...) = nur in "bash/ksh" erlaubt
# Haeufig zum Fuellen von Variablen aus Kmdo-Ergebnissen genutzt!
#-------------------------------------------------------------------------------
echo
DATUM=`date +%d.%m.%Y`                    # Datum in Variable speichern
UHRZEIT=$(date +%T)                       # Uhrzeit in Variable speichern
echo "DATUM UHRZEIT=$DATUM $UHRZEIT"      #
LISTE=$(ls -1)                            # Dateiliste in Variable speichern
echo "LISTE=$LISTE"                       #

#-------------------------------------------------------------------------------
# 7) Prozess-Substitution
# * Kmdo-Standard-Ausgabe (stdout) als (temporaere) Datei benutzen: <(KMDO)
# * Kmdo-Standard-Eingabe (stdin)  als (temporaere) Datei benutzen: >(KMDO)
#-------------------------------------------------------------------------------
cat /etc/hosts |
	sed 's/127.0.0.[0-9]/127.0.0.0/' |
	sort |
	diff - <(sort /etc/hosts)             # - = stdin, <(...) = temp. Datei

diff <(type) <(type -a)                   # Temp. Datei(en) fuer Kommando-stdout
                                          # erzeugen + als Parameter an Kmdo
                                          # uebergeben, das aus Datei liest

awk 'BEGIN {                              # Temp. Datei fuer Kommando-stdin
	file = ARGV[1]                        # erzeugen + als Parameter an Kmdo
	print file > "/dev/stderr"            # uebergeben, das in Datei schreibt
	print "hallo"  > file                 #
	print "welt"   > file                 #
	print "ist"    > file                 #
	print "so"     > file                 #
	print "schoen" > file                 #
}' >(sort -r)                             #

#-------------------------------------------------------------------------------
# 7b) Datei-Substitution
# Inhalt von Datei als String in Skript einbetten: $(<DATEI)
# VAR=$(<DATEI) entspricht VAR=$(cat datei), ist aber schneller
#-------------------------------------------------------------------------------
VAR=$(</etc/passwd)                       # Inhalt von Datei "/etc/hostname"
echo "$VAR"                               # lesen und in Variable speichern
VAR=$(cat /etc/passwd)                    # Analog (Prozess gestartet: langsamer)
echo "$VAR"                               #

#-------------------------------------------------------------------------------
# 8) Bedingungen ueberpruefen ("test" liefert Exit-Code "0/1" = "wahr/falsch",
# der in "if", "while" und "until" als Entscheidungsgrundlage dient)
# IMMER Gaensefuesschen bei Variablenzugriff (wg. leerer Var. --> Syntaxfehler!)
# IMMER Leerzeichen um Operatoren (z.B. -r) und Operanden (z.B. /tmp) angeben!
# Im folgenden ist mit "Datei" immer auch "Verz." gemeint (und umgekehrt)
# Statt "test ..." auch "[ ... ]" oder "[[ ... ]]" verwendbar (besser lesbar)
# (BASH = nur in der Bash vorhanden)
#-------------------------------------------------------------------------------
echo
# (A) Zugriffsrechte testen
test -r /etc/passwd                       # Datei lesbar (-r=readable)
test -w /tmp                              # Verz. schreibbar (-w=writable)
[ -w /tmp ]                               # Analog (besser lesbar!)
test -x /usr/bin/bash                     # Datei ausfuehrbar (-x=executable)
test -u /usr/bin                          # Verz. hat Set-User-ID gesetzt (UID)
test -g /usr/bin                          # Verz. hat Set-Group-ID gesetzt (GID)
test -k /usr/bin                          # Verz. hat Sticky-Bit gesetzt

# (B) Datei-Besitzer und Besitzer-Gruppe testen
test -O /usr/lib/include                  # Effektiver User = Datei-Besitzer? (BASH)
test -G /usr/lib/include                  # Eff. Gruppe = Datei-Besitzergruppe? (BASH)

# (C) Datei-Existenz und -Groesse testen
test -a /usr/lib/include                  # Datei existiert (-a=available,BASH)
test -e /usr/lib/include                  # Datei existiert (-e=existent)
test -s /usr/bin/bash                     # Datei ist NICHT leer (-s=size)
test ! -s /usr/bin/bash                   # Datei ist leer (-s=size)

# (D) Datei-Alter testen
test -N /usr/bin                          # Datei seit letztem Lesen geaendert (New,BASH)
test /etc -nt /var                        # VZ1 neuer als VZ2 (newer than,BASH)
test /etc -ot /var                        # VZ1 aelter als VZ2 (older than,BASH)
test /tmp -ef /var/tmp                    # D1 identisch D2 (equal file,BASH)
                                          # (Hardlink=Inode gleich)
# (E) Datei-Typen testen
# (IMMER -L=Link VOR allen anderen Tests durchfuehren!)
test -L /usr/bin                          # Symbol. Link  (-L=symlink)
test -h /usr/bin                          # Symbol. Link  (-h=?,BASH)
test -f /etc/shadow                       # Datei         (-f=file)
test -d /usr/bin                          # Verzeichnis   (-d=directory)
test -c /dev/stdin                        # Zeichengeraet (-c=character)
test -b /dev/sda1                         # Blockgeraet   (-b=block)
test -S /tmp/file                         # Socket        (-S=socket)
test -p /tmp/npipe                        # Named Pipe    (-p=pipe)
test -t /dev/tty1                         # Terminal      (-t=tty)

# (F) Strings vergleichen (Variablenzugriff IMMER in Gaensefuesschen setzen!)
test "$TEXT"                              # Variable NICHT leer (Kurzform)
test -n "$TEXT"                           # Variable NICHT leer (-n=nonzero)
test -z "$TEXT"                           # Variable leer (-z=zero)
test "$TEXT" = "ja"                       # Variable gleich Text
test "$TEXT" == "ja"                      # Variable gleich Text (BASH)
test "$TEXT" != "ja"                      # Variable ungleich Text
test "$TEXT" \< "ja"                      # Var. alph. kl. als "ja" (BASH)
test "$TEXT" \> "ja"                      # Var. alph. gr. als "ja" (BASH)

# (G) Zahlen vergleichen (NICHT "=" und "!=" = Textvergleich verwenden!)
ZAHL=10                                   # Var. init. (sonst Syntaxfehler!)
test "$ZAHL" -eq 10                       # Variable gleich 10 (-eq=equal)
test "$ZAHL" -ne 10                       # Var. ungleich 10 (-ne=not equal)
test "$ZAHL" -gt 10                       # Var. gr. als 10 (-gt=greater than)
test "$ZAHL" -ge 10                       # Gr./gl. 10 (-ge=greater or equal)
test "$ZAHL" -lt 10                       # Var. kleiner 10 (-lt=less than)
test "$ZAHL" -le 10                       # Kl./gleich 10 (-le=less or equal)

# (H) Shelloptionen pruefen
test -o nocasematch                       # Shell-Option aktiviert (on, BASH)
test ! -o nocasematch                     # Shell-Option deaktiv. (not on, BASH)

# (I) Logische Verknuepfungen
test 1 -o 2                               # Oder-Verknuepfung (or)
test 1 -a 2                               # Und-Verknuepfung (and)
test ! 1                                  # Nicht-Verknuepfung (not)
test \( 1 -o 2 \) -a 3 -a !0              # Klammerung (normal: ! vor -a vor -o)

# (J) Sonstiges
test -v VAR                               # Shell-Variable existiert? (variable,BASH)
test ! -v VAR                             # Shell-Variable NICHT existent ? (variable,BASH)

#-------------------------------------------------------------------------------
# 9) Weitere Schreibweise fuer Bedingungs-Test (besser lesbar):
# * Schluesselwort "test" weglassen,
#   statt dessen die eckige Klammern [ ... ] oder [[...]] um Bedingung setzen
# * UNBEDINGT Leerzeichen um "[", "]", "[[", "]]" und "-le" setzen!
# * NIE test und [...] oder [[...]] gleichzeitig verwenden!
#-------------------------------------------------------------------------------
[ -w /tmp ]                               # Datei schreibbar (w=writable)
[ "$ZAHL" -le 10 ]                        # Kl./gleich 10 (le=less or equal)
[[ "$ZAHL" -le 10 ]]                      # Kl./gleich 10 (le=less or equal)

#-------------------------------------------------------------------------------
# 10) Regulaere Ausdruecke vergleichen
# * Nur per [[...]], nicht per [...] und "test" moeglich
# * "..." NICHT um Regex --> Stringvergleich (Metazeichen = normale Zeichen)
# * " " mit \ schuetzen, nicht mit "..."!
# * !~ gibt es nicht (passt nicht zu) --> ! "TEXT" =~ REGEX verwenden
# * Array BASH_REMATCH[.] enthaelt Matches geklammerter Regexteile
#   ([0] = ganzer Match, [1] = 1.Klammer von links, ...)
#-------------------------------------------------------------------------------
shopt -u nocasematch                      # GROSS/kleinschreibung beachten (-u=unset)
[[ "Hallo" =~ a.*o ]]   && echo "Ja"      # Passt zu       --> Ja
[[ "Hallo" =~ A.*o ]]   && echo "Nein"    # Passt zu       --> Nein
[[ ! "Hallo" =~ a.*o ]] && echo "Nein"    # Passt NICHT zu --> Nein
[[ ! "Hallo" =~ A.*o ]] && echo "Ja"      # Passt NICHT zu --> Ja
[[ ! "$TEXT" =~ \  ]]   && echo "Ja"      # Enthaelt Leerzeichen

shopt -s nocasematch                      # GROSS/kleinschreibung ign. (-s=set)
[[ "Hallo" =~ a.*o ]]   && echo "Ja"      # Passt zu       --> Ja
[[ "Hallo" =~ A.*o ]]   && echo "Ja"      # Passt zu       --> Ja
[[ ! "Hallo" =~ a.*o ]] && echo "Ja"      # Passt NICHT zu --> Ja
[[ ! "Hallo" =~ A.*o ]] && echo "Ja"      # Passt NICHT zu --> Ja
[[ ! "$TEXT" =~ \  ]]   && echo "Ja"      # Enthaelt kein Leerzeichen

[[ "Gut aber gern" =~ (u.*e)(.*e) ]]      # Match durchfuehren und merken
echo "Gesamtmatch: ${BASH_REMATCH[0]}"    # Gesamttreffer
echo "1. Klammer: ${BASH_REMATCH[1]}"     # 1. Klammertreffer
echo "2. Klammer: ${BASH_REMATCH[2]}"     # 2. Klammertreffer

#-------------------------------------------------------------------------------
# 11) Rechnen mit Ganzzahlen (eigentlich kann die Shell nicht rechnen bzw. kennt
#     keine Zahlvariablen, Zahlen sind Strings bestehend aus Ziffern)
# * VAR=$(expr ...)          # Kmdo, Space, Op. quoten, immer da, old school
# * declare -i VAR; VAR=...  # Builtin (bash,ksh),  no  Space, no $
# * let VAR=...              # Builtin (bash,ksh), (no) Space, no $
# * VAR=$((...))             # Builtin (bash,ksh), (no) Space, no $
# * VAR=$[...]               # Builtin (bash,ksh),      Space, no $
#-------------------------------------------------------------------------------
# expr
ZAHL=0                                    # Klassisch per expr (Shell kann nicht)
ZAHL=$(expr $ZAHL + 1 \* 9 % 5 - 4 / 2)   # Leerzeichen NOTWENDIG!
echo "ZAHL=$ZAHL"
#ZAHL=$(expr ZAHL + 1 \* 9 % 5 - 4 / 2)   # Variable OHNE "$" NICHT erlaubt!
#echo "ZAHL=$ZAHL"

# declare -i
START=111
declare -i ZAHL                           # Variable vom Typ -i=Integer
ZAHL=$START+111                           # KEINE Leerzeichen erlaubt!
echo "ZAHL=\$START+111 --> $ZAHL"
ZAHL=START+222                            # Variable OHNE "$" erlaubt!
echo "ZAHL=\$START+222 --> $ZAHL"
ZAHL="START + 333"                        # MIT "..." Leerzeichen erlaubt
echo "ZAHL=\"\$START + 333\" --> $ZAHL"

# let
unset ZAHL
let ZAHL=$START+444                       # KEINE Leerzeichen erlaubt!
echo "let ZAHL=\$START+444 --> $ZAHL"
let ZAHL=START+555                        # Variable OHNE "$" erlaubt!
echo "let ZAHL=START+555 --> $ZAHL"
let ZAHL="START + 666"                    # Variable OHNE "$" erlaubt!
echo "let ZAHL=\"\$START + 666\" --> $ZAHL"# MIT "..." Leerzeichen erlaubt!

# $((...)) ist identisch zu "let"!        # (MODERN!)
ZAHL=$(($START+777))                      # MIT und OHNE Leerzeichen erlaubt!
echo "ZAHL=\$(($START+777)) --> $ZAHL"
ZAHL=$((START+888))                       # Variable OHNE "$" erlaubt!
echo "ZAHL=\$((START+888)) --> $ZAHL"
ZAHL=$(( START + 999 ))                   # MIT und OHNE Leerzeichen erlaubt!
echo "ZAHL=\$(( \$START + 999 )) --> $ZAHL"

# $[...]                                  # (veraltet!)
ZAHL=$[$START+777]                        # MIT und OHNE Leerzeichen
echo "ZAHL=\$[ $START + 777 ] --> $ZAHL"
ZAHL=$[ START + 888 ]                     # Variable OHNE "$" erlaubt!
echo "ZAHL=\$[ $START + 888 ] --> $ZAHL"

#-------------------------------------------------------------------------------
# 12) Rechenoperatoren bei ((...)) (bzw. let; Arithmetischer Ausdruck, nur bash/ksh)
# * Exit-Status ist 0, wenn Ergebnis des arithm. Ausdrucks != 0 ist!
# * Exit-Status ist 1, wenn Ergebnis des arithm. Ausdrucks == 0 ist!
# * Operatoren: + - * / % **    ++ --    || && !    | & ^ ~ << >>
#               == != < <= > >=    ?:
#-------------------------------------------------------------------------------
echo
 ZAHL=34
START=12

ERG=$(( $ZAHL + $START ))                 # + = Addition
echo "$ZAHL + $START --> $ERG"

ERG=$(( $ZAHL - $START ))                 # - = Subtraktion
echo "$ZAHL - $START --> $ERG"

ERG=$(( $ZAHL * $START ))                 # * = Multiplikation
echo "$ZAHL * $START --> $ERG"

ERG=$(( $ZAHL / $START ))                 # / = Division
echo "$ZAHL / $START --> $ERG"

ERG=$(( $ZAHL % $START ))                 # % = Modulo (Divisionsrest)
echo "$ZAHL % $START --> $ERG"

ERG=$(( $ZAHL ** $START ))                # ** = Potenzierung
echo "$ZAHL ** $START --> $ERG"

echo -n "++ $ERG --> "
((++ERG))                                 # ++ = Inkrement (Praefix = Pre)
echo "$ERG"

echo -n "$ERG ++ --> "
((ERG++))                                 # ++ = Inkrement (Postfix = Post)
echo "$ERG"

echo -n "$ERG -- --> "
((--ERG))                                 # -- = Dekrement (Praefix = Pre)
echo "$ERG"

echo -n "$ERG -- --> "
((ERG--))                                 # -- = Dekrement (Postfix = Post)
echo "$ERG"

ERG=$(( $ZAHL || $START ))                # || = logisch OR
echo "$ZAHL || $START --> $ERG"

ERG=$(( $ZAHL && $START ))                # && = logisch AND
echo "$ZAHL && $START --> $ERG"

ERG=$(( ! $ZAHL ))                        # ! = logisch NOT
echo "! $ZAHL --> $ERG"

ERG=$(( $ZAHL | $START ))                 # | = bitweise OR
echo "$ZAHL | $START --> $ERG"

ERG=$(( $ZAHL & $START ))                 # & = bitweise AND
echo "$ZAHL & $START --> $ERG"

ERG=$(( $ZAHL ^ $START ))                 # ^ = bitweise XOR
echo "$ZAHL ^ $START --> $ERG"

ERG=$(( ~ $ZAHL ))                        # ~ = bitweise NOT
echo "~ $ZAHL --> $ERG"

ERG=$(( $ZAHL << 2 ))                     # << = bitweise SHIFT left
echo "$ZAHL << 2 --> $ERG"

ERG=$(( $ZAHL == 2 ))                     # == = gleich
echo "$ZAHL == 2 --> $ERG"

ERG=$(( $ZAHL != 2 ))                     # != = ungleich
echo "$ZAHL != 2 --> $ERG"

ERG=$(( $ZAHL < 2 ))                      # < = kleiner
echo "$ZAHL < 2 --> $ERG"

ERG=$(( $ZAHL > 2 ))                      # > = groesser
echo "$ZAHL > 2 --> $ERG"

ERG=$(( $ZAHL <= 2 ))                     # <= = kleiner gleich
echo "$ZAHL <= 2 --> $ERG"

ERG=$(( $ZAHL >= 2 ))                     # >= = groesser gleich
echo "$ZAHL >= 2 --> $ERG"

ERG=$(( $ZAHL ? 2 : -2))                  # ?: = bedingter Ausdruck
echo "$ZAHL ? 2 : -2 --> $ERG"

#-------------------------------------------------------------------------------
# 13) Rechnen mit Fliesskommazahlen ("bc" oder "dc")
#     bc: Beliebige Genauigkeit, beliebige Basis ()
#     dc: Umgekehrte/Reverse polnische Notation (desc calculator, UPN/RPN)
#-------------------------------------------------------------------------------
FLT=$(echo "0.1 + 0.2 - 0.3 * 0.4" | bc -l)   # -l=mathlib
echo "BC: $FLT"

PI=$(echo "scale=10; 4*a(1)" | bc -l)         # 10 Nachkommastellen: scale 10
echo "PI: $PI"                                # Arcus Tangens:       a()

FLT=$(dc -e "10 k; 2.1 3.4 + p" 2> /dev/null) # 10 Nachkommastellen: 10 k
echo "DC: $FLT"                               # Ausdruck: -e EXPR
                                              # Ausgabe(print): p

ERG=$(expr 41 \* 95)                          # Kann keine Fliesskommazahlen
echo "ERG: $ERG"                              # 
ERG=$((41 * 95))                              # Kann keine Fliesskommazahlen
echo "ERG: $ERG"                              # 
ERG=$(bc <<< "4.1 * 9.2")                     # <<< "..." = Here-String
echo "ERG: $ERG"                              # 
ERG=$(dc -e "4.1 9.2 * p")                    # RPN/UPN (p=print)
echo "ERG: $ERG"                              # 

#-------------------------------------------------------------------------------
# 14) Logische Operatoren bei [[...]] (Bedingter Ausdruck, nur bash/ksh)
# * Rueckgabe von 0/1=wahr/falsch abhaengig vom Ergebnis des bedingten Ausdrucks
# * Zerlegung in Token und Pfadnamen-Expansion FINDEN NICHT STATT
# * Tilde-Expansion, Parameter/Variable-Expansion, Arithmetische Expansion,
#   Kommando-Substitution, Prozess-Substitution, Entfernen der Quotierung
#   FINDEN STATT
# * < und > sortieren alphabetisch (lexikografisch) gemaess "locale"
# * == und != erlauben RECHTS ein Shell-Muster ? [...] [^...] * ** 
# * =~ fuehrt Vergleich mit Regulaerem Ausdruck auf RECHTER Seite durch
#-------------------------------------------------------------------------------
[[ $ZAHL = 4 ]]
echo "[[ $ZAHL = 4 ]] --> $?"

#-------------------------------------------------------------------------------
# 14b) Moegliche Operatoren in [[...]]
#-------------------------------------------------------------------------------
# -a FILE
# -b FILE
# -c FILE
# -d FILE
# -e FILE
# -f FILE
# -g FILE
# -h FILE
# -k FILE
# -p FILE
# -r FILE
# -s FILE
# -t FD  # Wahr falls Dateideskriptor FD offen und auf ein Terminal verweist
# -u FILE
# -w FILE
# -x FILE
# -O FILE
# -G FILE
# -L FILE
# -S FILE
# -N FILE
# FILE1 -nt FILE2
# FILE1 -ot FILE2
# FILE1 -ef FILE2
# -o OPTNAME
# -z STRING
# STRING
# -n STRING
# STRING1 == STRING2
# STRING1 =  STRING2
# STRING1 != STRING2
# STRING1 <  STRING2
# STRING1 >  STRING2
# ARG1 -eq ARG2
# ARG1 -ne ARG2
# ARG1 -lt ARG2
# ARG1 -le ARG2
# ARG1 -gt ARG2
# ARG1 -ge ARG2
#-------------------------------------------------------------------------------
# TODO

#-------------------------------------------------------------------------------
# 15) if-then-elif-else-Verzweigung
#-------------------------------------------------------------------------------
echo
ZAHL=10                                   # Leerz. um "=" --> Syntaxfehler!
if [ $ZAHL -gt 2 ]                        # Leerzeichen um "[", "]" und "-gt"!
then                                      # "then" muss auf neuer Zeile stehen!
	echo "if: $ZAHL groesser als 2"
elif [ $ZAHL -lt 2 ]                      # NICHT "else if" "elseif" "elsif"!
then                                      # "then" muss auf neuer Zeile stehen!
	echo "if: $ZAHL kleiner als 2"
else
	echo "if: $ZAHL gleich 2"
fi                                        # "fi" nicht vergessen (neue Zeile)!

#-------------------------------------------------------------------------------
# 16) for-Schleife (Wort-Liste abarbeiten)
# (ACHTUNG: Hinter line-continuation "\" darf KEIN Zeichen mehr folgen)
#-------------------------------------------------------------------------------
echo
# for ELEM in *.txt *.doc *.lst
for ELEM in abc defghi jklmnopqr stu \
            vw xy z                       # ELEM enthaelt Werte aus Wort-Liste
do                                        # "do" muss auf neuer Zeile stehen!
	echo "for: ELEM=$ELEM"
done                                      # "done" nicht vergessen (neue Zeile)!

#-------------------------------------------------------------------------------
# 17) for-Schleife (Aufruf-Argumente abarbeiten)
#-------------------------------------------------------------------------------
echo
for ARG                                   # ARG enthaelt nacheinand. Shell-Arg.
do                                        # "do" muss auf neuer Zeile stehen!
	echo "for: ARG=$ARG"
done                                      # "done" nicht vergessen (neue Zeile)!

#-------------------------------------------------------------------------------
# 18) while-do-Schleife (Schleife durchlaufen, SOLANGE BEDINGUNG erfuellt)
#-------------------------------------------------------------------------------
echo
ZAEHLER=3
while [ $ZAEHLER -gt 0 ]
do                                        # "do" muss auf neuer Zeile stehen!
	echo "while: ZAEHLER=$ZAEHLER"
	ZAEHLER=$(expr $ZAEHLER - 1)          # Kommando-Subst (Leerz. um "-" notw.)
done                                      # "done" nicht vergessen (neue Zeile)!

#-------------------------------------------------------------------------------
# 19) until-do-Schleife (Schleife durchlaufen, BIS BEDINGUNG erfuellt)
#-------------------------------------------------------------------------------
echo
until [ $ZAEHLER -eq 3 ]
do                                        # "do" muss auf neuer Zeile stehen!
	echo "until: ZAEHLER=$ZAEHLER"
	ZAEHLER=$(expr $ZAEHLER + 1)          # Kommando-Subst (Leerz. um "+" notw.)
done                                      # "done" nicht vergessen (neue Zeile)!

#-------------------------------------------------------------------------------
# 20) repeat-until-Schleife GIBT ES NICHT!
#-------------------------------------------------------------------------------
# GIBT ES NICHT!

#-------------------------------------------------------------------------------
# 21) Einfache Schleife vorzeitig neu starten bzw. abbrechen
# (mehrere Anweisungen mit ";" statt mit Newline zu trennen ist erlaubt!)
#-------------------------------------------------------------------------------
echo
for I in 1 2 3 4 5 6 7 8 9 10
do
	if [ "$I" -eq 3 ]; then continue; fi  # Schleife bei "3" auf naechsten Wert
	if [ "$I" -eq 7 ]; then break; fi     # Schleife bei "7" abbrechen
	echo "I=$I"
done

#-------------------------------------------------------------------------------
# 22) Verschachtelte Schleife vorzeitig neu starten bzw. abbrechen
# * "continue" entspricht "continue 1"
# * "break"    entspricht "break 1"
#-------------------------------------------------------------------------------
echo
for I in 1 2 3 4 5 6 7 8 9 10    # Schleife 2
do
	for J in 1 2 3 4 5 6 7 8 9 10    # Schleife 1
	do
		[ "$I" = 8 -a "$J" = 8 ] && break 2
		[ "$J" -gt "$I" ]        && continue 2
		echo "I=$I J=$J"
	done
	echo
done

#-------------------------------------------------------------------------------
# 23) Zaehlschleife (eher ungewoehnlich in der Shell)
#-------------------------------------------------------------------------------
echo
for N in 1 2 3 4 5 6 7 8 9 10             # Aufsteigend 1 2 ... 10
do                                        #
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

echo
for N in 10 9 8 7 6 5 4 3 2 1             # Absteigend 10 9 ... 1
do                                        #
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

echo
for N in $(seq 10)                        # Aufsteigend 1 2 ... 10 (oder "jot")
do                                        # (auch "seq 1 10" oder "seq 1 1 10")
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

echo
for N in {1,2,3,4,5,6,7,8,9,10}           # Aufsteigend 1 2 ... 10
do                                        #
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

echo
for N in {1..10}                          # Aufsteigend 1 2 ... 10
do                                        #
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

echo
for (( N = 1; N <= 10; ++N ))             # Aufsteigend 1 2 ... 10 (Zaehler)
do                                        # (analog "for" in C, nur bash!)
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

echo
for (( N = 10; N > 0; --N ))              # Absteigend 10 9 ... 1 (Zaehler)
do                                        # (analog "for" in C, nur bash!)
	echo "$N. Durchlauf"
done
echo "Endwert: N=$N"

#-------------------------------------------------------------------------------
# 24) Interaktive Auswahl aus einer Liste (PS3 = Promptstring)
#-------------------------------------------------------------------------------
echo
PS3=">>> "
echo "Bitte per Nummer auswaehlen:"
select AUSWAHL in "deutsch" "englisch" "franzoesisch"
do
	if [ -n "$AUSWAHL" ]
	then
		break;
    else
        echo "Bitte noch mal!" 1>&2
	fi
done
echo "selected: $AUSWAHL ($REPLY)"

#-------------------------------------------------------------------------------
# 25) case-Mehrfachverzweigung (Text-Vergleich mit Shell-Metazeichen)
# In Reihenfolge der Muster verglichen, Anweisungen nach 1. zutreffenden Muster
# werden ausgefuehrt, else-Zweig per "*)" als letztem Muster realisieren.
#-------------------------------------------------------------------------------
echo
TEXT="Moses"                              # Zum Testen Zeilen vertauschen
TEXT="Mulut"                              # Zum Testen Zeilen vertauschen
TEXT="Anton"                              # Zum Testen Zeilen vertauschen
TEXT="Babel"                              # Zum Testen Zeilen vertauschen
TEXT=
case "$TEXT" in                           # "in" nicht vergessen!
	A*) echo "case: Anton gefunden" ;;    # Abschluss ";;" nicht vergessen!
	M*) echo "case: Moses oder Mulut gefunden"
		;;                                # Abschluss ";;" nicht vergessen!
	*)  echo "case: Babel gefunden" ;;    # else-Zweig
esac                                      # Abschluss "esac" nicht vergessen
                                          # (neue Zeile)!
#-------------------------------------------------------------------------------
# 26) Label und "goto" SIND NICHT MOEGLICH!
# (toll, und das bei einer ueber 30 Jahre alten Programmiersprache!)
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
# 27) Funktions-Definition ohne Parameter (VOR Funktions-Aufruf notwendig!)
# TIPP: Geschweifte Klammern auf extra Zeile setzen (sonst ";" notwendig!)
# Kein expliziter Exit-Status zurueckgegeben --> Exit-Status des letzten Kmdos
#-------------------------------------------------------------------------------
func1()                                   # Leere "()" ohne Leerz. notwendig
{                                         # Extra Zeile!
	echo "Hier ist func1()"               # Einruecken sinnvoll
}                                         # Extra Zeile!

#-------------------------------------------------------------------------------
# Funktions-Definition (VOR Funktions-Aufruf notwendig!)
# TIPP: Geschweifte Klammern auf extra Zeile setzen (sonst ";" notwendig!)
#-------------------------------------------------------------------------------
func2()                                   #
{                                         #
	echo "Hier ist func2()"               # Rueckgabe eines Wertes
	return 123                            # Rueckgabe eines Exit-Status
}                                         #

#-------------------------------------------------------------------------------
# 28) Funktions-Aufruf OHNE Uebergabe von Argumenten (NACH Funktions-Definition!)
# Zum Aufruf nur Name der Funktion angeben, keine Klammern!
#-------------------------------------------------------------------------------
echo
echo "Aufruf von func1 ohne Argumente"
func1
echo "Exit-Status von func1(): $?"        # 1x abfragbar (Variante A)

#-------------------------------------------------------------------------------
# Funktions-Aufruf OHNE Uebergabe von Argumenten (NACH Funktions-Definition!)
# Zum Aufruf nur Name der Funktion angeben, keine Klammern!
#-------------------------------------------------------------------------------
echo
echo "Aufruf von func2 ohne Argumente mit Rueckgabe eines Wertes"
RESULT=$(func2)                           # Abfangen der Standardausgabe
                                          # (Fehlerausgabe nicht)
echo "Exit-Status von func2(): $?"        # 1x abfragbar (Variante A)
echo "Rueckgabewert von func2(): $RESULT" # Von stdout abgefangener Text

#-------------------------------------------------------------------------------
# 29) Funktions-Definition mit Nutzung von Positions-Parametern $1 $2 $3 ...
# (die Zeichen "<" und ">" dienen nur zur Hervorhebung!)
#-------------------------------------------------------------------------------
function func2()                          # "function" optional angebbar
{                                         # Extra Zeile!
	echo "Hier ist func2()"               # Einruecken sinnvoll
	echo "\$* Alle Funktions-Parameter: <$*>" # Variante A
	echo "\$@ Alle Funktions-Parameter: <$@>" # Variante B
	echo "\$# Anzahl Funktions-Parameter: <$#>"
	echo "\$1 1. Funktions-Parameter: <$1>"
	echo "\$2 2. Funktions-Parameter: <$2>"
	echo "\$3 3. Funktions-Parameter: <$3>"
	echo "\$4 4. Funktions-Parameter: <$4>"
	echo "\$5 5. Funktions-Parameter: <$5>"
	echo "\$6 6. Funktions-Parameter: <$6>"
	echo "\$7 7. Funktions-Parameter: <$7>"
	echo "\$8 8. Funktions-Parameter: <$8>"
	echo "\$9 9. Funktions-Parameter: <$9>"
    echo
}                                         # Extra Zeile!

#-------------------------------------------------------------------------------
# 30) Funktions-Aufruf MIT Uebergabe von Argumenten
#-------------------------------------------------------------------------------
echo
echo "Aufruf von func2 mit Argumenten"
func2 Dies "" ist "ein Test" "   " 1 2 3 4 5 6 7 8 9 10

#-------------------------------------------------------------------------------
# 31) Skript-Aufruf-Argumente/Parameter verarbeiten (SKRIPT PARAM1 PARMA2 ...)
#-------------------------------------------------------------------------------
echo "\$0 Skript-Name:                   $0"  # Im Skript verwendbar!
echo "\$* Alle Skript-Parameter:         $*"  # Variante A
echo "\$@ Alle Skript-Parameter:         $@"  # Variante B
echo "\$# Anzahl Skript-Parameter:       $#"
echo "\$1 1. Skript-Parameter (1. Wort): $1"
echo "\$2 2. Skript-Parameter (2. Wort): $2"
echo "\$3 3. Skript-Parameter (3. Wort): $3"
echo "\$4 4. Skript-Parameter (4. Wort): $4"
echo "\$5 5. Skript-Parameter (5. Wort): $5"
echo "\$6 6. Skript-Parameter (6. Wort): $6"
echo "\$7 7. Skript-Parameter (7. Wort): $7"
echo "\$8 8. Skript-Parameter (8. Wort): $8"
echo "\$9 9. Skript-Parameter (9. Wort): $9"
echo

shift 3
echo "\$@ Alle Skript-Parameter nach shift 3: $@"   # Variante B

#-------------------------------------------------------------------------------
# 31a) Usage-Meldung (evtl. wird eine Fehler-Meldung in "$1" uebergeben)
#-------------------------------------------------------------------------------
Usage()
{
	exec 1>&2   # Alle Ausgaben auf Fehlerkanal umlenken

	[ -n "$1" ] && echo "error: $1"
	echo "usage: $(basename $0) [OPTION...] [FILE...]"
	echo "       -a      ..."
	echo "       -e VAL  ..."
	echo "       -h      Usage-Meldung erzeugen"

	exit 1
}

#-------------------------------------------------------------------------------
# 31b) Je nachdem, ob Skript-Parameter angegeben oder nicht, etwas anderes tun
#-------------------------------------------------------------------------------
echo
if [ $# -eq 0 ]
then
	echo "KEINE Skript-Parameter angegeben, setze selber welche!"
	set -- Param im Skript setzen, da keine von aussen mitgegeben
	echo "Skript-Parameter INNEN gesetzt: $# --> $*"
else
	echo "Skript-Parameter von AUSSEN mitgegeben: $# --> $*"
fi

#-------------------------------------------------------------------------------
# 31c) Aufruf-Optionen verarbeiten
#-------------------------------------------------------------------------------
# Bekannte Optionen erkennen, falsche abfangen
while getopts ":ae:h" OPT    # ":" am Anfang unterdrueckt Fehlermeldung
do                           # ":" nach Buchstabe heisst mit Parameter
	case "$OPT" in
		a) echo "Option -a" ;;
		e) echo "Option -e mit Wert '$OPTARG'" ;;
		h) echo "Option -h"
		   Usage ;;
		*) Usage "Option '-$OPTARG' nicht erlaubt" ;;
	esac
done

# Optionen rauswerfen, restliche Argumente anzeigen
shift $(expr $OPTIND - 1)
echo "Argumente($#): $*"

# Parameterliste exakt reproduzieren ($*, $@, "$*" und "$@" stimmen nicht ganz)
# (typischerweise zum Aufruf eines weiteren Skriptes)
echo "Skript-Aufruf mit exakt ident. Parametern:" subscript.sh ${1+"$@"}

# Austausch des aktuellen Skripts gegen das neue
echo "Skript-Ersatz mit exakt ident. Parametern:" exec subscript.sh ${1+"$@"}

#-------------------------------------------------------------------------------
# 31d) Skript autom. mit "root"-Rechten neu aufrufen (oder abbrechen) falls nicht so:
#-------------------------------------------------------------------------------
#echo
#[ $(id -u) != 0 ] && echo "you have to be root!" 1>&2 && exec sudo $0 ${1+"$@"}
#[ $(id -u) != 0 ] && echo "you have to be root!" 1>&2 && exit 1

#-------------------------------------------------------------------------------
# 32) Weitere spezielle Skript-Variablen
#-------------------------------------------------------------------------------
echo
echo "\$? Letzter Exit-Status:                $?"       # <= Nur 1x lesbar, dann
echo "\$status Letzter Exit-Status:           $status"  #   ist Exit-Status des
echo "\$\$ Prozessnummer der Skript-Shell:     $$"      #   naechsten Kommandos
sleep 10 &                                              #   darin zu finden.
echo "\$! Nummer letzter Hintergrund-Prozess: $!"
echo "\$- Beim Aufruf angeg. Shell-Optionen:  $-"

#-------------------------------------------------------------------------------
# 33) Bedingte Variablen (- = + ? bzw. :- := :+ :?)
#
# +------------------------+---------+------------------+
# |                        | defined | defined or empty |
# +------------------------+---------+------------------+
# | Use    default Value   |    -    |        :-        |
# | Assign default Value   |    =    |        :=        |
# | Use    alternate Value |    +    |        :+        |
# | Error if undefined     |    ?    |        :?        |
# +------------------------+---------+------------------+
#-------------------------------------------------------------------------------
echo
# Variablen koennen 3 verschiedene Zustaende haben
# (-u=undefined bricht Skript bei Zugriff auf undefinierte Variable ab)
unset VU           # 1) VU = undefined (bei Zugriff als "leer" behandelt)
      VL=""        # 2) VL = empty     (definiert aber "leer")
      VG=Wert      # 3) VG = gefuellt  (definiert und nicht "leer")
echo "VU=undefined VL=leer VG=gefuellt"
echo "VU=$VU  VL=$VL  VG=$VG"

echo
echo "Default falls undefiniert (leer ist auch definiert): "
echo "-  VU=$VU"      "VL=$VL"      "VG=$VG"
echo "-  VU=${VU-Dflt} VL=${VL-Dflt} VG=${VG-Dflt}"
echo "-  VU=$VU"      "VL=$VL"      "VG=$VG"

echo
echo "Default + setzen falls undefiniert (leer ist auch definiert): "
echo "=  VU=$VU"      "VL=$VL"      "VG=$VG"
echo "=  VU=${VU=Dflt} VL=${VL=Dflt} VG=${VG=Dflt}"
echo "=  VU=$VU"      "VL=$VL"      "VG=$VG"

unset VU           # 1) VU = undefiniert
      VL=""        # 2) VL = leer (definiert)
      VG=Wert      # 3) VG = gefuellt (definiert)

echo
echo "Abbruch falls undefiniert: "
echo "?  VU=$VU"      "VL=$VL"      "VG=$VG"
#echo "?  VU=${VU?VU undef.} VL=${VL?VL undef.} VG=${VG?VG undef.}"
echo "?  VU=$VU"      "VL=$VL"      "VG=$VG"

echo
echo "Default falls definiert (leer ist auch definiert): "
echo "+  VU=$VU"      "VL=$VL"      "VG=$VG"
echo "+  VU=${VU+Dflt} VL=${VL+Dflt} VG=${VG+Dflt}"
echo "+  VU=$VU"      "VL=$VL"      "VG=$VG"

echo
echo "Default falls undefiniert oder leer: "
echo ":- VU=$VU"       "VL=$VL"       "VG=$VG"
echo ":- VU=${VU:-Dflt} VL=${VL:-Dflt} VG=${VG:-Dflt}"
echo ":- VU=$VU"       "VL=$VL"       "VG=$VG"

echo
echo "Default + setzen falls undefiniert oder leer: "
echo ":= VU=$VU"       "VL=$VL"       "VG=$VG"
echo ":= VU=${VU:=Dflt} VL=${VL:=Dflt} VG=${VG:=Dflt}"
echo ":= VU=$VU"       "VL=$VL"       "VG=$VG"

unset VU           # 1) VU = undefiniert
      VL=""        # 2) VL = leer (definiert)
      VG=Wert      # 3) VG = gefuellt (definiert)

echo
echo "Default falls definiert und nicht leer: "
echo ":+ VU=$VU"       "VL=$VL"       "VG=$VG"
echo ":+ VU=${VU:+Dflt} VL=${VL:+Dflt} VG=${VG:+Dflt}"
echo ":+ VU=$VU"       "VL=$VL"       "VG=$VG"

echo
echo "Abbruch falls undefiniert oder leer: "
echo ":? VU=$VU"       "VL=$VL"       "VG=$VG"
#echo ":? VU=${VU:?VU undef.} VL=${VL:?VL undef.} VG=${VG:?VG undef.}"
echo ":? VU=$VU"       "VL=$VL"       "VG=$VG"

#-------------------------------------------------------------------------------
# 34) Variablen-Manipulationen (String-Operationen)
# ACHTUNG: Shell-Muster (Globbing) statt Regulaere Ausdruecke
#-------------------------------------------------------------------------------
#     0123456789
TEXT="abcdefghij"                         # ACHTUNG: 1.Zeichen hat Index 0
echo
echo ":  $TEXT --> ${TEXT:4}"             # Substring ab Offset 4 (0=1.Zeichen)
echo ":: $TEXT --> ${TEXT:4:3}"           # Substring ab Offset 4 der Laenge 3

echo
echo "\$# $TEXT --> ${#TEXT}"             # Stringlaenge ermitteln

echo
TEXT="abcdefghijabcdefghij"
echo "#  $TEXT --> ${TEXT#abc*def}"       # Praefix entfernen (kuerzesten)
echo "## $TEXT --> ${TEXT##abc*def}"      # Praefix entfernen (laengsten)

echo
echo "%  $TEXT --> ${TEXT%def*hij}"       # Suffix entfernen (kuerzesten)
echo "%% $TEXT --> ${TEXT%%def*hij}"      # Suffix entfernen (laengsten)

echo
echo "/  $TEXT --> ${TEXT/def*abc}"       # Stringteil entfernen (durch nix ersetzen)
echo "/  $TEXT --> ${TEXT/def*abc/XXX}"   # Passenden Stringteil ersetzen
echo "// $TEXT --> ${TEXT//def*abc/XXX}"  # Alle passenden Stringteil ersetzen
echo "/# $TEXT --> ${TEXT/#def*abc/XXX}"  # Passenden Stringteil am Anfang ersetzen
echo "/% $TEXT --> ${TEXT/%def*abc/XXX}"  # Passenden Stringteil am Ende ersetzen

echo
TEXT="abcdefghij"
echo "^  $TEXT --> ${TEXT^}"              # In GROSSCHREIBUNG umwandeln (1 Zei)
echo "^^ $TEXT --> ${TEXT^^}"             # In GROSSCHREIBUNG umwandeln (Alle)
echo "^^ $TEXT --> ${TEXT^^[aeiou]}"      # In GROSSCHREIBUNG umwandeln (Liste)

echo
TEXT="ABCDEFGHIJ"
echo ",  $TEXT --> ${TEXT,}"              # In kleinschreibung umwandeln (1 Zei)
echo ",, $TEXT --> ${TEXT,,}"             # In kleinschreibung umwandeln (Alle)
echo ",, $TEXT --> ${TEXT,,[AEIOU]}"      # In kleinschreibung umwandeln (Liste)

#-------------------------------------------------------------------------------
# 35) Verzweigung abhaengig vom Skript-Namen
# (Kommando-Substitution per `...` ueberall, per $(...) nur in "bash" erlaubt)
#-------------------------------------------------------------------------------
echo
NAME=`basename $0 .sh`                    # Pfad + Endung ".sh" entfernen
NAME=$(basename $0 .sh)                   # Pfad + Endung ".sh" entfernen

if [ $0 = "bashcode" ]
then
	echo "Skript heisst 'bashcode'"
else
	echo "Name des Skriptes lautet '$NAME'"
fi

echo "Pfad des Skriptes: `dirname $0`"    # Pfadanteil ausgeben (Kmdo-Subst.)
echo "Pfad des Skriptes: $(dirname $0)"   # Pfadanteil ausgeben (Kmdo-Subst.)

#-------------------------------------------------------------------------------
# 36) Indiziertes Array
# * Indices sind:
#   + numerisch von 0 bis unendlich oder --> declare -a moeglich (nicht noetig)
#   + beliebiger Text (assoziativ, Hash) --> declare -A notwendig
# * Arrays sind 1-dimensional
# * Ihre Groesse ist beliebig
# * Numerische Indices muessen nicht aufeinanderfolgen
# * Anzahl Elemente ist nicht beschraenkt
#-------------------------------------------------------------------------------
echo
declare -a NUM                            # Num. Array dekl. (nicht noetig)

NUM[1]=123                                # Ein Element fuellen
NUM[2]="xyz"                              # Ein Element fuellen
NUM[3]=789                                # Ein Element fuellen
                                          # Zugriff mit {...} notwendig
echo "NUM[1]=${NUM[1]}"                   # Element ausgeben, {...} notwendig
echo "NUM[2]=${NUM[2]}"                   # Element ausgeben, {...} notwendig
echo "NUM[3]=${NUM[3]}"                   # Element ausgeben, {...} notwendig
echo "ANZa=${#NUM[*]} NUM=${NUM[*]}"      # Arraylaenge + -Inhalt ausgeben

NUM[1]+=111                               # Stringverkettung
NUM[2]+="abc"                             # Stringverkettung
NUM[3]+="def"                             # Stringverkettung
echo "NUM[1]=${NUM[1]}"                   # Element ausgeben, {...} notwendig
echo "NUM[2]=${NUM[2]}"                   # Element ausgeben, {...} notwendig
echo "NUM[3]=${NUM[3]}"                   # Element ausgeben, {...} notwendig
echo "ANZb=${#NUM[*]} NUM=${NUM[*]}"      # Arraylaenge + -Inhalt ausgeben

unset NUM                                 # Array loeschen (Datentyp)
declare -i NUM                            # Integer-Array dekl. (kein String)

NUM[1]+=111                               # Numerisch addieren
NUM[2]+="abc"                             # Numerisch addieren
NUM[3]+=111                               # Numerisch addieren
echo "NUM[1]=${NUM[1]}"                   # Element ausgeben, {...} notwendig
echo "NUM[2]=${NUM[2]}"                   # Element ausgeben, {...} notwendig
echo "NUM[3]=${NUM[3]}"                   # Element ausgeben, {...} notwendig
echo "ANZc=${#NUM[*]} NUM=${NUM[*]}"      # Arraylaenge + -Inhalt ausgeben

unset NUM                                 # Array loeschen (Datentyp)
NUM=(v1 v2 v3 v4 v5 v6)                   # Array = Elemente 0..5 fuellen
echo "NUM[0]=${NUM[0]}"                   # Element ausgeben, {...} notwendig
echo "NUM[0]=$NUM"                        # Identisch zu ${NUM[0]}
echo "NUM[1]=${NUM[1]}"                   # Element ausgeben, {...} notwendig
echo "NUM[2]=${NUM[2]}"                   # Element ausgeben, {...} notwendig
echo "NUM[3]=${NUM[3]}"                   # Element ausgeben, {...} notwendig
echo "NUM[4]=${NUM[4]}"                   # Element ausgeben, {...} notwendig
echo "NUM[5]=${NUM[5]}"                   # Element ausgeben, {...} notwendig
echo "NUM[6]=${NUM[6]}"                   # Leeres Elem. ausgeben, {...} notwendig
echo "ANZd=${#NUM[@]} NUM=${NUM[*]}"      # Arraylaenge + -Inhalt ausgeben

NUM=([0]=val1 [3]=val2 [9]=val3)
echo "ANZe=${#NUM[*]} NUM=${NUM[@]}"      # Arraylaenge + -Inhalt ausgeben

NUM=([0]=val1 val2 val3 [3]=val4 val5 val6 [9]=val7)
echo "ANZf=${#NUM[@]} NUM=${NUM[@]}"      # Arraylaenge + -Inhalt ausgeben

unset NUM[1]                              # Zweites Element loeschen
echo "ANZg=${#NUM[*]} NUM=${NUM[*]}"      # Arraylaenge + -Inhalt ausgeben

unset NUM                                 # Array loeschen (Datentyp)
echo "ANZh=${#NUM[@]} NUM=${NUM[@]}"      # Arraylaenge + -Inhalt ausgeben

for N in 1 2 3 4 5 6 7 8 9 10             # 10 Arrayelemente fuellen
do
	NUM[$N]="`expr $N \* $N`"
done

for N in 1 2 3 4 5 6 7 8 9 10             # 10 Arrayelemente ausgeben
do
	echo "NUM[$N] = ${NUM[$N]}"
done
echo "ANZi=${#NUM[*]} NUM=${NUM[@]}"      # Arraylaenge + -Inhalt ausgeben

echo -en "\nBitte Wortliste eingeben: "   # Textstuecke in Array einlesen
read -a INP < <(echo "abc def ghi")       # Eingabe in Array einlesen (-a)
for N in $(seq 0 ${#INP[*]})              # Gelesene Elemente + 1 ausgeben
do
	echo "INP[$N]=${INP[$N]}"
done
echo "ANZj=${#INP[@]} INP=${INP[*]}"      # Arraylaenge + -Inhalt ausgeben

NUM[0]="null"                             # Ok (Index 0)
#NUM[-1]="minus1"                         # Nicht erlaubt (Index -1)
echo "NUM[0]=${NUM[0]}"
echo "ANZk=${#NUM[@]} NUM=${NUM[@]}"      # Arraylaenge + -Inhalt ausgeben

# Hohe Indices benutzen
for N in 0 1 2 4 8 16 32 64 128 256 512 \
         1024 2048 4096 8192 16384 32768 65536
do
	NUM[$N]="`expr $N \* $N`"
done
for N in 0 1 2 4 8 16 32 64 128 256 512 \
         1024 2048 4096 8192 16384 32768 65536
do
	echo "NUM[$N]  = ${NUM[$N]}"
done
echo "ANZl=${#NUM[*]} NUM=${NUM[@]}"      # Arraylaenge + -Inhalt ausgeben

#-------------------------------------------------------------------------------
# 37) Assoziatives Array
# * Anzahl Elemente ist nicht beschraenkt
#-------------------------------------------------------------------------------
echo
declare -A ASS                              # Assoz. Array deklarieren (notw.)

ASS[a]=123                                  # Ein Element fuellen
ASS[bc]="xyz"                               # Ein Element fuellen
ASS[def]=789                                # Ein Element fuellen
echo "ASS[a]=${ASS[a]}"                     # Elem. ausgeben, {...} notwendig
echo "ASS[bc]=${ASS[bc]}"                   # Elem. ausgeben, {...} notwendig
echo "ASS[def]=${ASS[def]}"                 # Elem. ausgeben, {...} notwendig

ASS=([a]=v1 [def]=v2 [uvw]=v3)              # Drei Elemente fuellen
echo "ASS[a]=${ASS[a]}"                     # Elem. ausgeben, {...} notwendig
echo "ASS[bc]=${ASS[bc]}"                   # Elem. ausgeben, {...} notwendig
echo "ASS[def]=${ASS[def]}"                 # Elem. ausgeben, {...} notwendig
echo "ASS[uvw]=${ASS[uvw]}"                 # Elem. ausgeben, {...} notwendig
echo "ASS[xyz]=${ASS[xyz]}"                 # Leeres Element
echo "ANZm=${#ASS[*]} ASS=${ASS[*]}"        # Arraylaenge + -Inhalt ausgeben

for N in a bc def uvw xyz                   # Elem. ausgeben
do
	echo "ASS[$N] = ${ASS[$N]}"
done
echo "ANZn=${#ASS[*]} ASS=${ASS[*]}"        # Arraylaenge + -Inhalt ausgeben

unset ASS[def]                              # Ein Element loeschen
echo "ANZo=${#ASS[*]} ASS=${ASS[*]}"        # Arraylaenge + -Inhalt ausgeben

unset ASS                                   # Array loeschen
echo "ANZp=${#ASS[@]} ASS=${ASS[@]}"        # Arraylaenge + -Inhalt ausgeben

#-------------------------------------------------------------------------------
# 38) Temporaere(s) Datei/Verz. anlegen + am Skriptende automatisch loeschen
#-------------------------------------------------------------------------------
# Namen von temp. Dateien/Verz. nicht erratbar generieren und anlegen
# (XXX...X wird durch zufaellige Zeichenkette ersetzt)
umask 077
 TMPDIR=$(mktemp -d /tmp/sec-XXXXXXXXXXX) || exit 1 # Temp. Verz. (mind. 12 X!)
TMPFILE=$(mktemp $TMPDIR/sec-XXXXXXXXXXX) || exit 1 # Temp. Datei (mind. 12 X!)
umask 022

# Temporaere Dateien/Verz. bei Skriptende (Signal 0/EXIT) automatisch loeschen
trap 'rm -rf $TMPFILE $TMPDIR > /dev/null 2>&1' EXIT
trap 'rm -rf $TMPFILE $TMPDIR > /dev/null 2>&1' 0

#-------------------------------------------------------------------------------
# 39) Betriebssystem-Abfrage
#-------------------------------------------------------------------------------
if [[ "$OSTYPE" =~ linux ]]       # Linux ("linux-gnu")
then
	echo "Linux"
elif [[ "$OSTYPE" =~ darwin ]]    # Apple Mac OS X ("darwin14.5")
then
	echo "OSX"
elif [[ "$OSTYPE" =~ cygwin ]]    # Cygwin unter Windows
then
	echo "Cygwin"
elif [[ "$OSTYPE" =~ msys ]]      # MinGW unter Windows
then
	echo "Mingw32"
elif [[ "$OSTYPE" =~ solaris ]]   # SUN Solaris
then
	echo "Solaris"
elif [[ "$OSTYPE" =~ freebsd ]]   # FreeBSD
then
	echo "FreeBSD"
elif [[ "$OSTYPE" =~ netbsd ]]    # NetBSD
then
	echo "NetBSD"
elif [[ "$OSTYPE" =~ openbsd ]]   # OpenBSD
then
	echo "OpenBSD"
elif [[ "$OSTYPE" =~ bsd ]]       # BSD
then
	echo "Mingw32"
else
	echo "Unknown"
fi

#-------------------------------------------------------------------------------
# 40) Tipps
#-------------------------------------------------------------------------------
# Gruppe + alle Anderen auf im Skript erz. Dateien/Verz. nicht zugreifen lassen
umask 077

# Namen von temp. Dateien/Verz. nicht erratbar generieren und anlegen
 TMPDIR=$(mktemp -d /tmp/secshXXXXXXXXXXX) || exit 1 # Temp. Verz. (mind. 12 X!)
TMPFILE=$(mktemp $TMPDIR/secshXXXXXXXXXXX) || exit 1 # Temp. Datei (mind. 12 X!)

# Gewuenschter Benutzer(-Gruppe) zuordnen + Abbruch wenn nicht moeglich
chgrp $GROUP $TMPDIR $TMPFILE || exit 1

# Wichtige Signale abfangen
trap "exit 1" 0    1   2   3    13   14   15   17   # --> Signal 0 = Skript-Ende ausloesen
trap "exit 1" EXIT HUP INT QUIT PIPE ALRM TERM CHLD # --> Signal 0 = Skript-Ende ausloesen

# Temporaere Dateien/Verz. bei Skriptende (Signal 0/EXIT) automatisch loeschen
trap 'rm -rf $TMPFILE $TMPDIR > /dev/null 2>&1' EXIT
trap 'rm -rf $TMPFILE $TMPDIR > /dev/null 2>&1' 0

# WICHTIG: Verz.-Wechsel per "cd" IMMER pruefen + Abbruch wenn nicht moeglich!
cd "$WORKDIR" || exit 1

# Debugging anschalten?
[ "$1" = "-d" ] && DBG=1

# Wertzuweisung und Variablenverwendung immer mit "..." aussenrum
# (leer oder Metazeichen --> Peng!)
VAR="Wert"
echo "VAR=$VAR"

# Vergleiche immer gegen leere Variablen UND Optionen schuetzen
if [ "$VAR" = "abc" ]         # Leerraum und Sonderzeichen schuetzen
then
    :
fi

if [ "X$VAR" = "Xabc" ]       # Falls Variablenwert mit "-" beginnt!
then
    :
fi

# Dateilisten mit "/dev/null" abschliessen
# (vermeidet leere Liste --> Warten auf Benutzereingabe)
#cat * /dev/null
#cat $* /dev/null

#-------------------------------------------------------------------------------
# Skript abbrechen mit Exit-Code 100
#-------------------------------------------------------------------------------
exit 100
