Benutzer-Werkzeuge

Webseiten-Werkzeuge


bash

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
bash [2025-08-31 13:29:14] – [bc] manfredbash [2025-08-31 22:08:18] (aktuell) – [Zahlen von eine Basis in eine andere Basis umrechnen] manfred
Zeile 1: Zeile 1:
 +====== bash (Bourne SHell / Bourne-Again SHell) ======
 +
 +In jedem Unix-Ähnlichen System findet sich die klassische //Bourne SHell// unter ///bin/sh//.
 +
 +Aus diesem Grund scripte ich mit Vorliebe für diese Shell. Dann laufen die Scripte nicht nur auf Linux, sondern auch auf NetBSD, FreeBSD, Solaris, AIX und anderen Unix-Ähnlichen Betriebssystemen.
 +
 +[[http://www.linux-praxis.de/linux1/shell2_7.html]]
 +
 +[[http://www.netzmafia.de/skripten/unix/unix2.html]]
 +
 +
 +===== farbige Textausgabe (24bit Farben) =====
 +
 +  * [[https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters|SGR Parameter]]
 +  * [[https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit|24-bit Farben]]
 +  * [[https://google.com/search?q=color+picker|Color Picker]]
 +
 +  ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩m   # RGB foreground color
 +  ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩m   # RGB background color
 +  ESC[0m                # reset
 +
 +  * ''ESC'': Darstellung mit ''\033''
 +  * 1. Paramter: ''38'' (Vordergrundfarbe) und ''48'' (Hintergrundfarbe)
 +  * 2. Paramter: ''2'' (RGB Format verwenden)
 +  * 3/4/5. Paramter: rot/grün/blau Werte mit jeweils 8-bit in dezimal (0 - 255), weil ''24 / 3 = 8''-bit
 +
 +
 +==== Beispiel um Syntax zu vereinfachen ====
 +
 +Variablen:
 +  * TEXT: Text der printf übergeben wird
 +  * XY
 +    * X: Vordergrund (F) oder Hintergrund (B)
 +    * Y: rot (R), grün (G) oder blau (B)
 +
 +Umgebungsvariable als Shortcut
 +  PRINT_COLOR='eval printf "\033[39m\033[49m"$(test ${#FG_RGB[@]} -eq 3 && printf "\033[38;2;${FG_RGB[0]};${FG_RGB[1]};${FG_RGB[2]}m")""$(test ${#BG_RGB[@]} -eq 3 && printf "\033[48;2;${BG_RGB[0]};${BG_RGB[1]};${BG_RGB[2]}m")"${TEXT}\033[0m"; unset TEXT FG_RGB BG_RGB;'
 +
 +Nutzung
 +  TEXT="Hello," FG_RGB=(255 255 255) BG_RGB=(0 0 0); $PRINT_COLOR && TEXT=" "; $PRINT_COLOR && TEXT="World!" FG_RGB=(0 0 0) BG_RGB=(255 255 255); $PRINT_COLOR && echo
 +  TEXT="Hello World!" FG_RGB=(0 0 0) BG_RGB=(255 255 255); $PRINT_COLOR && echo
 +  TEXT="Hello World!" FG_RGB=(127 0 255); $PRINT_COLOR && echo
 +  TEXT="Hello World!" BG_RGB=(127 0 255); $PRINT_COLOR && echo
 +  TEXT="Hello World!"; $PRINT_COLOR && echo
 +
 +**Achtung: Die Semikolon (;) sind wichtig!**
 +
 +
 +===== Bash - Pattern =====
 +
 +[[https://mywiki.wooledge.org/BashGuide/Patterns|BashGuide/Patterns]]
 +
 +
 +==== Klammererweiterung ====
 +
 +Dann gibt es die Klammererweiterung (Brace-Expansion). Klammererweiterung passt technisch nicht in die Kategorie der Muster, ist aber ähnlich.
 +Globs werden nur auf tatsächliche Dateinamen erweitert, aber Klammererweiterungen werden auf jede mögliche Permutation ihres Inhalts erweitert.
 +So funktionieren sie:
 +  $ echo th{e,a}n
 +  then than
 +  $ echo {/home/*,/root}/.*profile
 +  /home/axxo/.bash_profile /home/lhunath/.profile /root/.bash_profile /root/.profile
 +  $ echo {1..9}
 +  1 2 3 4 5 6 7 8 9
 +  $ echo {0,1}{0..9}
 +  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19
 +
 +Die Klammererweiterung wird durch eine Liste von Wörtern ersetzt, genau wie ein Glob. Diese Wörter sind jedoch nicht unbedingt Dateinamen, und sie sind nicht sortiert (als wären sie vorher gekommen).
 +
 +__Die Klammererweiterung erfolgt vor der Dateinamenerweiterung.__ Im zweiten echo-Befehl oben haben wir eine Kombination aus Klammererweiterung und Globs verwendet. Die Klammererweiterung geht zuerst und wir erhalten:
 +  $ echo /home/*/.*profile /root/.*profile
 +
 +Nach der Klammererweiterung werden die Globs erweitert und wir erhalten als Endergebnis die Dateinamen.
 +
 +Klammererweiterungen können nur verwendet werden, um Wortlisten zu generieren. Sie können nicht für den Mustervergleich verwendet werden.
 +
 +Es gibt einige interessante und nicht sehr intuitive Unterschiede zwischen den Bereichen in Zeichenklassen wie ''[a-z]'' und Klammererweiterung. Zum Beispiel erlaubt die Klammererweiterung das Rückwärtszählen, wie man bei ''{5..1}'' oder sogar ''{b..Y}'' sehen kann, während ''[5-1]'' nicht durch die Shell erweitert wird. ''[b-Y]'' kann je nach Gebietsschema erweitert werden oder nicht. Außerdem ignorieren Zeichenbereiche in Klammererweiterungen Gebietsschemavariablen wie LANG und LC_COLLATE und verwenden immer die ASCII-Reihenfolge. Globbing hingegen wird durch die Spracheinstellungen beeinflusst. Das folgende Fragment ist ein Beispiel für das Herunterzählen und die Anzeige von Zeichen in der Reihenfolge ihrer ASCII-Codes:
 +  $ echo {b..Y}
 +  b a ` _ ^ ]  [ Z Y
 +
 +
 +===== Feldtrennzeichen - Delimiter (IFS) =====
 +
 +[[https://www.cyberciti.biz/faq/unix-linux-bash-while-read-function-define-ifs-delimiter/|Bash / KSH: Define Delimiter (IFS) While Using read Command]]
 +
 +anzeigen
 +  cat -etv
 +
 +
 +===== Remote Procedure Call - RPC =====
 +
 +  * [[https://gist.github.com/derekp7|RPC mit BASH]] - Let's say you have a Bash shell script, and you need to run a series of operations on another system (such as via ssh).
 +    * [[https://gist.github.com/derekp7/9978986|rpcsh]] - So, to solve this, you can use a technique called rpcsh -- rpc in shell script...
 +
 +
 +===== automatische Ersetzung im Dateinamen =====
 +
 +Hier wird die Dateiendung ".text" in ".txt" getauscht:
 +  for Datei in *.mts; do mv -v "${Datei}" ${Datei%.text}.txt; done
 +  ffmpeg -i 00001.text 00001.txt
 +
 +
 +
 +===== Optionen =====
 +
 +  > man bash
 +  Laut Doku werden bei "$*" die Elemente durch $IFS (meisten der Zeilenumbruch) getrennt behandelt.
 +  Laut Doku werden bei "$@" die Elemente durch Leerzeichen getrennt behandelt.
 +  In meinen Test konnte ich bei beiden nur einer Erweiterung mit Leerzeichen erkennen.
 +
 +__Ich konnte keinen Unterschied zwischen ''"$*"'' und ''"$@"'' feststellen.__
 +
 +<file bash /tmp/test.sh>
 +#!/bin/bash
 +
 +echo '# 1: "$*"'
 +echo "$*"
 +echo '# 2: $*'
 +echo $*
 +
 +echo '# 3: "$@"'
 +echo "$@"
 +echo '# 4: $@'
 +echo $@
 +
 +echo '# 5: "$#"'
 +echo "$#"
 +echo '# 6: $#'
 +echo $#
 +echo '# 7: #'
 +
 +echo "$1"
 +echo "$2"
 +echo "$3"
 +echo "$4"
 +echo "$5"
 +echo "#------------------------------------------------------------------------------#"
 +echo $1
 +echo $2
 +echo $3
 +echo $4
 +echo $5
 +</file>
 +  > /tmp/test.sh 1 2 3 4 "5 6 7 8"
 +  # 1: "$*"
 +  1 2 3 4 5 6 7 8
 +  # 2: $*
 +  1 2 3 4 5 6 7 8
 +  # 3: "$@"
 +  1 2 3 4 5 6 7 8
 +  # 4: $@
 +  1 2 3 4 5 6 7 8
 +  # 5: "$#"
 +  5
 +  # 6: $#
 +  5
 +  # 7: #
 +  1
 +  2
 +  3
 +  4
 +  5 6 7 8
 +  #------------------------------------------------------------------------------#
 +  1
 +  2
 +  3
 +  4
 +  5 6 7 8
 +
 +
 +===== Funktionen =====
 +
 +In der Bash wird eine Funktion fast so gebaut wie in "C".
 +
 +Alle Variablen, die außerhalb der Funktion definiert wurden,
 +sind auch in der Funktion verwendbar:
 +  #!/usr/bin/env bash
 +  VARIABLE="01"
 +  funktionx()
 +  {
 +          echo "VARIABLE=${VARIABLE}"
 +  }
 +  funktionx
 +
 +Wird eine Skript-Datei mit z.B. zwei Parametern aufgerufen,
 +dann sind bekanntlich diese vier übergebenen Parameter in der Datei
 +als "//$1//" und "//$2//" verwendbar.
 +
 +Genauso verhält es sich mit den Parametern bei den Funktionen:
 +  #!/usr/bin/env bash
 +  NACHNAME="${1}"
 +  VORNAME="${2}"
 +  funktiony()
 +  {
 +          echo "
 +          FARBE='${1}'
 +          ELEMENT='${2}'
 +          VORNAME='${3}'
 +          NACHNAME='${4}'
 +          "
 +  }
 +  funktiony schwarze erde ${VORNAME} ${NACHNAME}
 +
 +Hier sind die Parameter, die der Funktion übergeben werden,
 +innerhalb der Funktion auch wieder als “$1“, “$2“, ... verwendbar.
 +
 +===== Entscheidungen =====
 +
 +Bei Entscheideidungen muss man unterscheiden, ob man Zahlen mit oder ohne Kommastellen als Vergleich verwenden möchte!
 +
 +Die "if"-Anweisung in der BASH kann nicht mit Kommastellen umgehen,
 +in soeinem Fall muss man dann auf AWK zurückgreifen.
 +
 +
 +==== BASH ====
 +
 +  if [ "${1}" < 1 ] ; then
 +        GR=klein
 +  elif [ "${1}" < 2 ] ; then
 +        GR=mittel
 +  elif [ "${1}" < 3 ] ; then
 +        GR=gross
 +  else
 +        GR="sehr gross"
 +  fi
 +  
 +  echo "${GR}"
 +
 +
 +==== AWK ====
 +
 +  # echo "1.5" | awk '{if ($1 < 3) GR="gross" ; if ($1 < 2) GR="mittel" ; if ($1 < 1) GR="klein" ; print $1"="GR}'
 +  1.5=mittel
 +
 +  # echo "1.5" | awk '{if ($1 < 3) GR=3 ; if ($1 < 2) GR=2 ; if ($1 < 1) GR=1 ; print GR}'
 +  2
 +
 +
 +===== Zählschleifen =====
 +
 +
 +==== BASH ====
 +
 +Unter Linux ist die //GNU Bourne-Again SHell// unter ///bin/bash// die Standard-Shell,
 +sie ist in erster Linie für die Konsolenarbeit verbesert worden.
 +So verfühgt sie im Gegensatz zur //Bourne SHell// über eine Historie.
 +
 +Die //GNU Bourne-Again SHell// verfühgt aber auch über zusätzliche Funktionen, die für die Arbeit mit Scripten wichtig sind.
 +
 +Zum Beispiel funktioniert der kleine Time-Out-Zähler nur in der //GNU Bourne-Again SHell//, nicht jedoch in der //Bourne SHell//:
 +
 +  #!/usr/bin/env bash
 +  WARTEZEIT="10"
 +  while [ ! -e /tmp/datei ]
 +  do
 +          sleep 1
 +          if [ "${WARTEZEIT}" -gt "0" ] ; then
 +                  WARTEZEIT=$((WARTEZEIT - 1))
 +          else
 +                  touch /tmp/datei
 +          fi
 +  done
 +
 +die folgenden beiden Zählschleifen funktionieren nur mit einer ''BASH'', nicht mit einer "echten ''sh''", wie z.B. die von FreeBSD (in Linux ist die ''sh'' ein Link auf die ''BASH''):
 +
 +  > i=0 ; while (("$i"<"10")) ; do i="$((${i}+1))" ; echo "${i}" ; done
 +  1
 +  2
 +  3
 +  4
 +  5
 +  6
 +  7
 +  8
 +  9
 +  10
 +
 +  > i=10;while (("$i">"0")); do echo "${i}"; i="$((${i}-1))"; done
 +  10
 +  9
 +  8
 +  7
 +  6
 +  5
 +  4
 +  3
 +  2
 +  1
 +
 +
 +=== sh ===
 +
 +Für die //Bourne SHell// muss das Kommando //bc// das rechnen im Script übernehmen:
 +
 +  #!/bin/sh
 +  WARTEZEIT="10"
 +  while [ ! -e /tmp/datei ]
 +  do
 +          sleep 1
 +          if [ "${WARTEZEIT}" -gt "0" ] ; then
 +                  WARTEZEIT="$(echo "${WARTEZEIT} - 1" | bc)"
 +          else
 +                  touch /tmp/datei
 +          fi
 +  done
 +
 +
 +oder das Kommando //awk//:
 +
 +  #!/bin/sh
 +  WARTEZEIT="10"
 +  while [ ! -e /tmp/datei ]
 +  do
 +          sleep 1
 +          if [ "${WARTEZEIT}" -gt "0" ] ; then
 +                  WARTEZEIT="$(echo "${WARTEZEIT}" | awk '{print $1-1}')"
 +          else
 +                  touch /tmp/datei
 +          fi
 +  done
 +
 +
 +=== bc ===
 +
 +natürlicher Logarithmus aus 5:
 +  > echo "l(5)" | bc -l
 +  1.60943791243410037460
 +
 +natürlicher Logarithmus aus 20:
 +  > echo "l(20)" | bc -l
 +  2.99573227355399099343
 +
 +dezimaler Logarithmus 1000:
 +  > echo "l(1000)/l(10)" | bc -l
 +  3.00000000000000000000
 +
 +binärer Logarithmus aus 8:
 +  > echo "l(8)/l(2)" | bc -l
 +  3.00000000000000000002
 +
 +  > echo "a=1; b=2; c=3; print \"a=\", a, \" b=\", b, \" c=\", c; print \"\nSumme:\", a + b + c;print \"\n\""
 +  a=1; b=2; c=3; print "a=", a, " b=", b, " c=", c; print "\nSumme:", a + b + c;print "\n"
 +  
 +  > echo "a=1; b=2; c=3; print \"a=\", a, \" b=\", b, \" c=\", c; print \"\nSumme:\", a + b + c;print \"\n\"" | bc -lq
 +  a=1 b=2 c=3
 +  Summe:6
 +
 +
 +=== Zahlen von eine Basis in eine andere Basis umrechnen ===
 +
 +  ibase = In welcher Basis verstehe ich deine Eingaben?
 +  obase = In welcher Basis soll ich die Ergebnisse ausgeben?
 +
 +  > echo "ibase=10; obase=2; 255" | bc
 +  11111111
 +
 +  > echo "255" | sed "s/.*/ibase=10; obase=2; &/" | bc
 +  11111111
 +
 +  > vim calc.bc
 +  ibase=10;
 +  obase=2;
 +  
 +  > echo "255" | bc calc.bc
 +  11111111
 +
 +<code c Zahlenbasis.bc>
 +obase = read();
 +n = read();
 +print n;
 +print "\n"
 +</code>
 +
 +<code bash>
 +> for BASIS in 2 3 5 7 8 12 16; do echo "${BASIS} 123" | tr ' ' '\n' | bc -q ./Zahlenbasis.bc; done
 +1111011
 +11120
 +443
 +234
 +173
 +A3
 +7B
 +</code>
 +
 +
 +=== Zähler mit while und bc ===
 +
 +Dieser Zähler ist zwar etwas umständlich, dafür funktioniert er aber auf jedem aktuellen Uni*-Like Betriebssystem, auf dem es das Programm "bc" gibt. ;-)
 +
 +Man muss nur darauf achten, dass //bc// installiert ist. Das kleine Rechenprogramm gibt es aber schon lange für alle Uni*-Like Betriebssysteme.
 +
 +
 +== vorwärts ==
 +
 +  # i=0;while(("$i"<"10"));do i="$(echo "1+$i"|bc)";echo "$i";done
 +  1
 +  2
 +  3
 +  4
 +  5
 +  6
 +  7
 +  8
 +  9
 +  10
 +
 +
 +== rückwärts ==
 +
 +  # i=10;while(("$i">"0"));do i="$(echo "$i-1"|bc)";echo "$i";done 
 +  9
 +  8
 +  7
 +  6
 +  5
 +  4
 +  3
 +  2
 +  1
 +  0
 +
 +
 +=== Zähler mit awk ===
 +
 +Sonst kann man auch //awk// als Rechenknecht verwenden.
 +//AWK// gibt es für alle Uni*-Like Betriebssysteme und ist eigentlich immer schon mit drauf.
 +
 +== vorwärts ==
 +
 +  # echo "10"|awk '{for(i=0;i<$1;i++) {print i}}'
 +  0
 +  1
 +  2
 +  3
 +  4
 +  5
 +  6
 +  7
 +  8
 +  9
 +
 +  # echo "10"|awk '{for(i=1;i<=$1;i++) {print i}}'
 +  1
 +  2
 +  3
 +  4
 +  5
 +  6
 +  7
 +  8
 +  9
 +  10
 +
 +hier in Zweierschritten:
 +  # echo "10"|awk '{for(i=1;i<=$1;i++) {if( i % 2 == 0) {print i}}}'
 +  2
 +  4
 +  6
 +  8
 +  10
 +
 +und jetzt mit fester Zahlenlänge:
 +
 +  # echo "10"|awk '{for(i=1;i<=$1;i++){printf("%.8u\n",i)}}'
 +  00000001
 +  00000002
 +  00000003
 +  00000004
 +  00000005
 +  00000006
 +  00000007
 +  00000008
 +  00000009
 +  00000010
 +
 +
 +== rückwärts ==
 +
 +  # i=10 ; while (( "$i" > "0")) ; do i="$(echo "$i" | awk '{print $1-1}')" ; echo "$i" ; done 
 +  9
 +  8
 +  7
 +  6
 +  5
 +  4
 +  3
 +  2
 +  1
 +  0
 +
 +  # i=10 ; until [ "$i" = "0" ] ; do i="$(echo "$i" | awk '{print $1-1}')" ; echo "$i" ; done 
 +  9
 +  8
 +  7
 +  6
 +  5
 +  4
 +  3
 +  2
 +  1
 +  0
 +
 +Oder man rechnet gleich komplett nur mit dem //awk//.
 +
 +  # echo "10"|awk '{for(i=$1;0<i;i--){print i}}'
 +  10
 +  9
 +  8
 +  7
 +  6
 +  5
 +  4
 +  3
 +  2
 +  1
 +
 +und jetzt mit fester Zahlenlänge:
 +
 +  # echo "10"|awk '{for(i=$1;0<i;i--){printf("%.8u\n",i)}}'
 +  00000010
 +  00000009
 +  00000008
 +  00000007
 +  00000006
 +  00000005
 +  00000004
 +  00000003
 +  00000002
 +  00000001
 +
 +
 +
 +
 +=== Zähler mit seq ===
 +
 +  * [[seq]]
 +
 +Dieser Befehl ist sehr bequem aber leider gibt es ihn nicht auf jedem System und ausserdem
 +ist er in Scripten manchmal recht zickig.
 +
 +== vorwärts ==
 +
 +  # seq 1 10
 +  1
 +  2
 +  3
 +  4
 +  5
 +  6
 +  7
 +  8
 +  9
 +  10
 +
 +== rückwärts ==
 +
 +  # seq 1 10 | tac
 +  10
 +  9
 +  8
 +  7
 +  6
 +  5
 +  4
 +  3
 +  2
 +  1
 +
 +
 +== Ausgabe mit fester Breite ==
 +
 +  # seq 1 10 | sed 's/.*/000000&/' | rev | cut -c 1-6 | rev
 +  000001
 +  000002
 +  000003
 +  000004
 +  000005
 +  000006
 +  000007
 +  000008
 +  000009
 +  000010
 +
 +
 +=== ewig warten bis ein Ereignis eintrifft ===
 +
 +In diesem Fall wartet die Schleife darauf, dass ein Rechner wieder über das Netz erreichbar ist.
 +
 +  #!/bin/sh
 +  
 +  START="1"
 +  while [ "$?" != "0" -o "${START}" = "1" ]
 +  do
 +          START="0"
 +          sleep 1
 +          ping -c1 192.168.0.100
 +  done
 +
 +  DATEI="diese_datei_wird_gleich_angelegt.tgz"
 +  while [ -r "${DATEI}" ]
 +  do
 +          sleep 1
 +  done
 +
 +
 +=== nur begrenzte Zeit warten bis ein Ereignis eintrifft ===
 +
 +nur "180" Sekunden lang auf die Datei warten:
 +<file bash>
 +#!/bin/sh
 +
 +DATEI="diese_datei_wird_gleich_angelegt.tgz"
 +ZAEHLER="180"
 +while [ ! -r "${DATEI}" -a "${ZAEHLER}" -gt "0" ]
 +do
 +        ZAEHLER="$(echo "${ZAEHLER}"|awk '{print $1-1}')"
 +        sleep 1
 +done
 +</file>
 +
 +oder so:
 +<file bash>
 +#!/bin/sh
 +
 +DATEI="diese_datei_wird_gleich_angelegt.tgz"
 +ZAEHLER="180"
 +while [ "${ZAEHLER}" -gt "0" ]
 +do
 +        ZAEHLER="$(echo "${ZAEHLER}"|awk '{print $1-1}')"
 +        if [ -r "${DATEI}" ] ; then
 +                ZAEHLER="0"
 +        fi
 +        sleep 1
 +done
 +</file>
 +
 +maximal 180 Sekunden lang wartet, bis die Datenbank erreichbar ist:
 +<file bash>
 +#!/usr/bin/env bash
 +
 +MYSQL_LOGIN="--defaults-file=/root/.my.cnf"
 +ZAEHLER="180"
 +
 +while [ "${ZAEHLER}" -gt "0" ]
 +do
 +        ZAEHLER="$(echo "${ZAEHLER}"|awk '{print $1-1}')"
 +        if [ "$(mysqlshow ${MYSQL_LOGIN})" ] ; then
 +                ZAEHLER="0"
 +        fi
 +        sleep 1
 +done
 +</file>
 +
 +
 +===== Parameter mit BASH =====
 +
 +  #!/usr/bin/env bash
 +  
 +  case "${1}" in
 +          [Ee][Ii][Nn])
 +                          SCHALTEN="ein"
 +                          shift
 +                          ;;
 +          [Aa][Uu][Ss])
 +                          SCHALTEN="aus"
 +                          shift
 +                          ;;
 +          *)
 +                          echo "${0} ein"
 +                          echo "${0} aus"
 +                          shift
 +                          exit 1
 +                          ;;
 +  esac
 +
 +  #!/usr/bin/env bash
 +  
 +  while [ "${#}" -ne "0" ]; do
 +          case "${1}" in
 +                  -a)
 +                          OPTION_A=${2}
 +                          shift
 +                          ;;
 +                  -b)
 +                          OPTION_B=${2}
 +                          shift
 +                          ;;
 +                  -h)
 +                          echo "
 +                          HILFE:
 +                          ${0} [Option]
 +                          -a [wert a]
 +                          -b [wert b]
 +                          "
 +                          exit 1
 +                          ;;
 +                  *)
 +                          if [ "$(echo "${1}"|egrep '^-')" ] ; then
 +                                  echo "Der Parameter '${1}' wird nicht unterstützt!"
 +                          fi
 +                          shift
 +                          ;;
 +          esac
 +  done
 +  
 +  echo "
 +  OPTION_A=${OPTION_A}
 +  OPTION_B=${OPTION_B}
 +  "
 +
 +Soll eine Fehlermeldung (''unbound variable'') erscheinen, wenn nicht alle Parameter übergeben wurden, dann muss "''BASH''" (in der ersten Zeile) mit der Optionen ''-u'' versehen werden.
 +
 +statt so
 +  #!/usr/bin/env bash
 +
 +kann es zum Beispiel so aussehen:
 +  #!/bin/bash -eu
 +
 +
 +==== Ein Beispiel mit getopts ====
 +
 +[[https://linuxhint.com/getopts-usage-example-linux/]]
 +
 +<file bash /tmp/beispiel_getopts.sh>
 +#!/bin/sh
 +
 +# wenn dieses Skript ohne Parameter aufgerufen wird
 +# dann wird eine Hilfe ausgegeben
 +if [ "x${1}" = x ] ; then
 +        echo "${0} -l -u [User] -h [Host]"
 +        exit 1
 +fi
 +
 +# der ":" besagt, dass dahinter ein Parameter erwartet wird
 +while getopts "u: h: l" OPTION
 +do
 +        case ${OPTION} in
 +        u)
 +                USER_NAME="${OPTARG}"
 +                ;;
 +        h)
 +                HOST_NAME="${OPTARG}"
 +                ;;
 +        l)
 +                STATUS="live"
 +                ;;
 +        esac
 +done
 +
 +### Der Parameter, der als Naechstes bearbeitet werden soll, wird bei einer Shell in der automatischen Variable OPTIND verwaltet.
 +### Der Wert dieser Variablen betraegt beim Aufruf erst einmal 1 – wird aber bei jedem weiteren getopts-Aufruf um 1 erhoeht.
 +### Wenn eine Kommandozeile mehrfach eingelesen werden soll, muss der Index manuell zurueckgesetzt werden.
 +#OPTIND=1
 +
 +echo "
 +OPTIND='${OPTIND}'
 +USER_NAME='${USER_NAME}'
 +HOST_NAME='${HOST_NAME}'
 +STATUS='${STATUS}'
 +"
 +</file>
 +
 +  > /tmp/beispiel_getopts.sh -l -u abc -h system
 +  
 +  OPTIND='6'
 +  USER_NAME='abc'
 +  HOST_NAME='system'
 +  STATUS='live'
 +
 +
 +===== while ohne sub-shell =====
 +
 +[[https://de.linux-console.net/?p=19969]]
 +
 +Auf die Variablen, die in einer gewöhnlichen ''while''-Schleife gesetzt werden,
 +hat man ausserhalb der Schleife keinen Zugriff, da die ''while''-Schleife
 +gewöhnlich mit einer Pipe gefüttert wird.
 +Wegen der Pipe wird aber eine eigene Shell geöffnet und mit ihrem Ende enden auch
 +die Variablen in ihr.
 +
 +Deshalb ist die Ausgabe dieses Skripts auch leer:
 +<code bash>
 +#!/usr/bin/env bash
 +
 +VAR=""
 +echo "Test" | while read ZEILE
 +do
 +        VAR="${ZEILE}"
 +done
 +
 +echo "VAR='${VAR}'"
 +</code>
 +
 +Dieses Skript liefert dagegen eine Ausgabe:
 +<code bash>
 +#!/usr/bin/env bash
 +
 +VAR=""
 +while read ZEILE
 +do
 +        VAR="${ZEILE}"
 +done < <(echo "Test")
 +
 +echo "VAR='${VAR}'"
 +</code>
 +
 +Keine Pipe,
 +keine Subshell,
 +keine gestobenen Variablen. ;-)
 +
 +Allserdings funktioniert das auch nicht in vollem Umfang:
 +<code bash>
 +#!/usr/bin/env bash
 +
 +VAR1=""
 +VAR2=""
 +VAR3=""
 +while read SPALTE1 SPALTE2 SPALTE3
 +do
 +        VAR1="${SPALTE1}"
 +        VAR2="${SPALTE2}"
 +        VAR3="${SPALTE3}"
 +done < <(echo "Test")
 +
 +echo "
 +VAR1='${VAR1}'
 +VAR2='${VAR2}'
 +VAR3='${VAR3}'
 +"
 +</code>
 +
 +Wenn die Zeile gleichzeitig noch in Spalten aufgetrennt werden soll,
 +was ja eigentlich geht,
 +dann versagt diese Methode... :-(
 +
 +
 +==== Probleme mit der while-Schleife ====
 +
 +führt man in einer WHILE-Schleife komplexe Kommandos aus, dann wird oft nur das erste Kommando ausgeführt oder jedes 2. Kommando nicht ausgeführt:
 +<code bash>
 +#!/usr/bin/env bash
 +
 +# bei jeder 2. Zeile (Kommando) fehlen an Zeilenanfang, mehrere Zeichen, deshalb bricht es ab
 +
 +find . -type f -iname "Air Strike_*.mkv" | sed 's|^[.]/||' | sort | while read FILE
 +do
 +        ffmpeg -i "${FILE}" -c:v mpeg4 -b:v 8192k -profile:v 15 -level 5 -g 300 -c:a ac3 -f avi -y "Test_${FILE}".avi
 +done
 +</code>
 +
 +Als Lösung kann man entweder den Umweg über eine Zwischendatei gehen, die dann als statisches Skript ausgeführt wird:
 +<code bash>
 +#!/usr/bin/env bash
 +
 +# funktioniert, wie erwartet
 +
 +find . -type f -iname "Air Strike_*.mkv" | sed 's|^[.]/||' | sort | while read FILE
 +do
 +        Iecho "ffmpeg -i \"${FILE}\" -c:v mpeg4 -b:v 8192k -profile:v 15 -level 5 -g 300 -c:a ac3 -f avi -y \"Test_${FILE}\".avi"
 +done > /tmp/statisches_Zwischen-Skript.sh
 +bash /tmp/statisches_Zwischen-Skript.sh
 +rm -f /tmp/statisches_Zwischen-Skript.sh
 +</code>
 +
 +Oder man wählt die FOR-Schleife, dann muß man aber den ''IFS'' verbiegen, um Dateien mit Leerzeichen verarbeiten zu können:
 +<code bash>
 +#!/usr/bin/env bash
 +
 +# funktioniert, wie erwartet, aber Dateien mit Leerzeichen kann man so nur mit verbogenem IFS verarbeiten
 +
 +IFS=$'\n'
 +DATEIEN=($(find . -type f -iname "Air Strike_*.mkv" | sed 's|^[.]/||' | sort))
 +for FILE in "${DATEIEN[@]}"
 +do
 +        ffmpeg -i "${FILE}" -c:v mpeg4 -b:v 8192k -profile:v 15 -level 5 -g 300 -c:a ac3 -f avi -y "Test_${FILE}".avi
 +done
 +</code>
 +
 +
 +===== leere Unterverzeichnisse löschen =====
 +
 +''rmdir'' löscht nur leere Verzeichnisse, bei vollen wird eine Fehlermeldung ausgegeben:
 +  > rmdir Verzeichnis/
 +
 +ohne Fehlermeldung kann man das so machen:
 +  > DIR="Verzeichnis/"; [ -d $DIR ] && [ $(ls -l $DIR | wc -l) -eq 1  ] && rmdir $DIR ||  :
 +
 +leeren Unterverzeichnisse kann man mit ''//find//'' auch __rekursiv__ löschen: [[find#leeren_unterverzeichnisse_loeschen]]
 +
 +
 +===== Alphabet generieren =====
 +
 +  > echo {a..z}
 +  a b c d e f g h i j k l m n o p q r s t u v w x y z
 +
 +
 +===== cooler Prompt =====
 +
 +Wenn man als privilegierter Benutzer angemeldet ist, ist der Benutzername im Prompt pink,
 +ist man als unprivilegierter Benutzer angemeldet ist, ist der Benutzername im Prompt hellblau.
 +Der Hostname ist immer dunkelblau.
 +
 +Andere Farben kann man nach belieben konfigurieren, die Farbtabelle ist u.a. hier hinterlegt: [[Terminaleinstellungen#kommandozeile_cli]]
 +
 +Die erste Zahl vor der Uhrzeit zeigt den Exit-Code an:
 +
 +  [root@rechner]---------------------------------------------------[0]-[0]-[14:14]
 +  [~]# false
 +  
 +  [root@rechner]---------------------------------------------------[1]-[0]-[14:14]
 +  [~]# 
 +
 +Die zweite Zahl gibt die Anzahl der im Hintergrund laufenden Job's an.
 +
 +  [root@rechner]---------------------------------------------------[0]-[0]-[14:15]
 +  [~]# sleep 60
 +  ^Z
 +  [1]+  Angehalten              sleep 60
 +  
 +  [root@rechner]-------------------------------------------------[148]-[1]-[14:15]
 +  [~]# bg 1
 +  [1]+ sleep 60 &
 +  
 +  [root@rechner]---------------------------------------------------[0]-[1]-[14:15]
 +  [~]# 
 +  
 +  [root@rechner]---------------------------------------------------[0]-[1]-[14:17]
 +  [~]# 
 +  [1]+  Fertig                  sleep 60
 +  
 +  [root@rechner]---------------------------------------------------[0]-[1]-[14:17]
 +  [~]# 
 +  
 +  [root@rechner]---------------------------------------------------[0]-[0]-[14:17]
 +  [~]# 
 +
 +
 +=== mach_bash_prompt.sh ===
 +
 +Dieses Skript nimmt den entsprechenden Eintrag in der datei ''~/.bashrc'' vor
 +und erstellt die Datei''~/.bash_prompt''.
 +
 +  #!/usr/bin/env bash
 +  
 +  echo "
 +  [ -f ~/.bash_prompt ] && . ~/.bash_prompt || PS1='\\[\\033[1m\\]\\u@\\h\\[\\033[0m\\]:\\w\\$ '
 +  export EDITOR=vi
 +  " >> ~/.bashrc
 +  
 +  (echo '
 +  PROMPT_COLORS=${PROMPT_COLORS:-0}
 +  
 +  prompt_command() {
 +      local EC="$?"
 +      
 +      case "$PROMPT_COLORS" in
 +          *)
 +              local CUSER="1;36"
 +              local CHOST="1;34"
 +              local CEC="1;34"
 +              local CJOBS="1;34"
 +              local CTIME="1;36"
 +              local CDIR="0;35"
 +              local CAT="0"
 +              local CWARN="1;35"
 +              local CDEF="0"
 +              ;;
 +      esac
 +      
 +      local R="\[\e[${CDEF}m\]"
 +      
 +      local USER=$(whoami)
 +      local HOSTNAME=$(hostname)
 +      local JOBS=$(jobs|wc -l)
 +      local PWD=$(pwd)
 +      local TIME="$(date +"%F %T")"
 +      
 +      PWD=${PWD/#$HOME/"~"}
 +      [ "${TERM:0:5}" == "xterm" ] && echo -en "\033]0;$USER@$HOSTNAME:$PWD\007"
 +      
 +      local TMP="[$USER@$HOSTNAME][$EC] [$JOBS] [$TIME]"
 +      [ -n "$SSH_CONNECTION" ] && TMP="$TMP [ssh]"
 +  
 +      local SP="-"
 +      [ ${#TMP} -gt 0 ] && local SPACER=$(printf "%$((COLUMNS - ${#TMP}))s") || local SPACER=" "
 +      SPACER=${SPACER// /$SP}
 +      
 +      [ $EC -gt 0 ] && EC="\[\e[${CWARN}m\]$EC" || EC="\[\e[${CEC}m\]$EC"
 +      [ $JOBS -gt 0 ] && JOBS="\[\e[${CWARN}m\]$JOBS" || JOBS="\[\e[${CEC}m\]$JOBS"
 +      [ $UID -eq 0 ] && USER="\[\e[${CWARN}m\]$USER" || USER="\[\e[${CUSER}m\]$USER"
 +      [ -n "$SSH_CONNECTION" ] && HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME$R]$SP[\[\e[${CWARN}m\]ssh" || HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME"
 +      
 +      PS1="
 +  $R[$USER\[\e[${CAT}m\]@$HOSTNAME$R]$SPACER\
 +  [$EC$R]$SP[$JOBS$R]$SP[\[\e[${CTIME}m\]$TIME$R]
 +  [\[\e[${CDIR}m\]$PWD$R]\\\$\[\e[0m\] "
 +  }
 +  
 +  PROMPT_COMMAND=prompt_command
 +  
 +  [ "${TERM:0:6}" == "screen" ] && {
 +      set_screen_title() {
 +          while [ 1 ] ; do
 +              case "$1" in
 +                  sudo|prompt_command|-*) shift ;;
 +                  *) break ;;
 +              esac
 +          done
 +          echo -en "\033k${1:-bash}\033\\"
 +      }
 +  '
 +  echo "    trap 'set_screen_title \$BASH_COMMAND' DEBUG
 +  }
 +  ") > ~/.bash_prompt
 +
 +
 +=== ~/.bashrc ===
 +
 +Dieser Eintrag überprüft, ob unsere spezielle BASH-Prompt-Datei vorhanden ist
 +und bindet sie ein, wenn es sie hier gibt.
 +
 +  [ -f ~/.bash_prompt ] && . ~/.bash_prompt || PS1='\[\033[1m\]\u@\h\[\033[0m\]:\w\$ '
 +  export EDITOR=vi
 +
 +
 +=== ~/.bash_prompt ===
 +
 +Diese Datei enthält die speziellen Einstellungen für unseren coolen Prompt.
 +
 +  PROMPT_COLORS=${PROMPT_COLORS:-0}
 +  
 +  prompt_command() {
 +      local EC="$?"
 +      
 +      case "$PROMPT_COLORS" in
 +          *)
 +              local CUSER="1;36"
 +              local CHOST="1;34"
 +              local CEC="1;34"
 +              local CJOBS="1;34"
 +              local CTIME="1;36"
 +              local CDIR="0;35"
 +              local CAT="0"
 +              local CWARN="1;35"
 +              local CDEF="0"
 +              ;;
 +      esac
 +      
 +      local R="\[\e[${CDEF}m\]"
 +      
 +      local USER=$(whoami)
 +      local HOSTNAME=$(hostname)
 +      local JOBS=$(jobs|wc -l)
 +      local PWD=$(pwd)
 +      local TIME=$(date +%H:%M)
 +      
 +      PWD=${PWD/#$HOME/"~"}
 +      [ "${TERM:0:5}" == "xterm" ] && echo -en "\033]0;$USER@$HOSTNAME:$PWD\007"
 +      
 +      local TMP="[$USER@$HOSTNAME][$EC] [$JOBS] [$TIME]"
 +      [ -n "$SSH_CONNECTION" ] && TMP="$TMP [ssh]"
 +  
 +      local SP="-"
 +      [ ${#TMP} -gt 0 ] && local SPACER=$(printf "%$((COLUMNS - ${#TMP}))s") || local SPACER=" "
 +      SPACER=${SPACER// /$SP}
 +      
 +      [ $EC -gt 0 ] && EC="\[\e[${CWARN}m\]$EC" || EC="\[\e[${CEC}m\]$EC"
 +      [ $JOBS -gt 0 ] && JOBS="\[\e[${CWARN}m\]$JOBS" || JOBS="\[\e[${CEC}m\]$JOBS"
 +      [ $UID -eq 0 ] && USER="\[\e[${CWARN}m\]$USER" || USER="\[\e[${CUSER}m\]$USER"
 +      [ -n "$SSH_CONNECTION" ] && HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME$R]$SP[\[\e[${CWARN}m\]ssh" || HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME"
 +      
 +      PS1="
 +  $R[$USER\[\e[${CAT}m\]@$HOSTNAME$R]$SPACER\
 +  [$EC$R]$SP[$JOBS$R]$SP[\[\e[${CTIME}m\]$TIME$R]
 +  [\[\e[${CDIR}m\]$PWD$R]\\\$\[\e[0m\] "
 +  }
 +  
 +  PROMPT_COMMAND=prompt_command
 +  
 +  [ "${TERM:0:6}" == "screen" ] && {
 +      set_screen_title() {
 +          while [ 1 ] ; do
 +              case "$1" in
 +                  sudo|prompt_command|-*) shift ;;
 +                  *) break ;;
 +              esac
 +          done
 +          echo -en "\033k${1:-bash}\033\\"
 +      }
 +      trap 'set_screen_title $BASH_COMMAND' DEBUG
 +  }
 +
 +
 +===== Array =====
 +
 +  Array (also mit Index): ${array[0]},     ${array[1]}
 +  Map   (also mit Key)  : ${array["one"]}, ${array["two"]}
 +
 +Array ist wie eine Liste, in der jedes Element ein Index hat und ''Map'' ist einfach eine Liste von Key-Value-Pairs. Theoretisch ist ersteres eine ''Map'', in der der Key ein Integer ist, der bei ''0'' startet.
 +
 +
 +==== mit explizitem Index ====
 +
 +
 +=== ReadArray ===
 +
 +  > readarray -t testarray < <(find Videos/ -type f)
 +  > echo "${testarray[0]}"
 +  > echo "${testarray[1]}"
 +
 +  > find Videos/ -type f > /tmp/test.txt
 +  > readarray -t testarray < /tmp/test.txt
 +  > echo "${testarray[0]}"
 +  > echo "${testarray[1]}"
 +
 +
 +==== mit implizitem Index ====
 +
 +  > export ARRAY=(127 0 255)
 +  > echo ${ARRAY[@]}
 +  127 0 255
 +  > echo ${#ARRAY[@]}
 +  3
 +  > echo ${#ARRAY[0]}
 +  127
 +  > echo ${#ARRAY[1]}
 +  0
 +  > echo ${#ARRAY[2]}
 +  255
 +
 +  > arrayLaenge=${#ARRAY[@]}
 +  > echo "Das Array enthält '${arrayLaenge}' Elemente."
 +  Das Array enthält '3' Elemente.
 +
 +  > testarray=($(find Videos/ -type f))
 +  > echo "${testarray[0]}"
 +  > echo "${testarray[1]}"
 +
 +**ACHTUNG!**
 +
 +__Es funktioniert mit **''testarray="$(find Videos/ -type f)"''** und **''testarray="($(find Videos/ -type f))"''** anders und der Index ist, bei diesen beiden, nicht das, was man erwartet!__
 +