Bash Shell
Freitag, 15. Oktober 2021
Fehler beim Ausführen von Shellscripts mit set beeinflussen
set -x | Ausgabe des aktuell ausgeführten Kommandos innerhalb des Skripts |
set -u | Skript abbrechen, falls eine verwendete Variable nicht deklariert ist |
set -e | Skript abbrechen, falls ein Kommando innerhalb des Scripts mit Fehler beendet wurde |
set -o pipefail | Stellt sicher, das bei einen Fehler innerhalb einer Pipe einen Exitstatus != 0 zurück gegeben wird |
.
Wichtige Builtin
shift [n] | rückt die aktuellen Parameter ("$1" usw.) um "n" weiter |
. ./script | liest script ein und führt deren Zeilen in der aktuellen Shell aus (wichtig, um die Variablen der aktuellen Shell beeinflussen zu können) |
exit ![n] | beendet das Skript und gibt den Exit-Code "n" (bzw. 0) zurück |
eval STRING | wertet STRING aus und führt die resultierende Zeichenkette aus |
.
Das Kommando trap
Mit trap kann auf Signale reagiert werden, um so z.B. den Programmabbruch zu verhindern.
Die Syntax lautet:
trap <action> <Signal>
- <aktion> Ein beliebiges Kommando in einfachen Anführungsstrichen
- <signal> Das Signal (Name oder Zahl) , das abgefangen werden soll. Eine Sonderrolle hat 0 als Skriptende.
Beispiel
#!/bin/bash trap 'echo SIGINT erhalten' 2 i=0 while [ $i -lt 5 ] do echo "Bitte nicht stören!" sleep 2 i=`expr $i + 1` done
In diesem Beispiel reagiert das Skript auf <crtl><c> mit der Meldung "SIGINT erhalten" - läuft aber weiter.
.
Das Kommando Test
Mit test kann die Ausführung von Kommandos ähnlich wie mit if/else Konstrukten gesteuert werden.
[ -f /tmp/sample.txt ] && { rm /tmp/sample.txt } || { echo Datei /tmp/sample.txt nicht vorhanden }
Mit dem Builtin Konstrukt [[ ]] und dem Operator =~ erlaubt die Bash Pattern Matching:
[[ $VAR =~ ^[0-9]*$ ]] && echo "is integer"
.
Eingabe-/Ausgabekanäle umleiten
Die 3 standard Ein- und Ausgabekanäle unter Linux
- stdin (Kanal 0)
- stdout (Kanal 1)
- stderr (Kanal 2)
Standard-Ausgabe in DATEI schreiben
echo "Zeile 1" > sample.txt # Default is stdout, deshalb nicht angegeben echo "Zeile 1" 1> sample.txt # Dito, Kanal explizit angegeben
Standard-Ausgabe an DATEI anhängen
echo "Zeile 2" >> sample.txt
Fehler-Ausgabe in DATEI schreiben
myscript.sh 2>/dev/null
Standard- und Fehlerausgabe in DATEI schreiben
myscript.sh &>/dev/null myscript.sh >/dev/null 2>&1 # Dito
Standard-Eingabe aus DATEI lesen
wc -l < sample.txt
String in die Standard-Eingabe schreiben (Here string)
bc <<< 5*4 # wie echo '5*4' | bc
Kanäle von Befehlsfolgen umleiten
(echo bar;echo foo)>/dev/null 2>&1 # keine Ausgabe
HERE Dokumente
Here Dokumente schreiben den Text zwischen dem Trenner (hier EOF) an stdin eines Kommandos (hier cat).
VAR="one" cat << EOF $VAR two three four five EOF # Variable $VAR wird aufgelöst
Sollen die Variablen-Substitution unterdrückt werden, muß der Trenner in einfachen Anführungszeichen gesetzt werden.
VAR="one" cat << 'EOF' $VAR two three four five EOF # Variable $VAR wird nicht aufgelöst (Ausgabe $VAR)
Im folgenden Beispiel wird die Ausgabe von cat in eine Datei umgeleitet
cat << EOF > /tmp/out.file one two three four five EOF
Process Substitution
wc < <(echo bar;echo foo) (echo foo;echo bar) | wc # Ausgabe wie oben
Befehls-Verkettungen
|| | ODER - Führt das folgende Kommando nur aus, wenn das erste nicht mit Fehlercode "0" beendet wurde |
&& | UND - Nur, falls das erste Kommando erfolgreich war. Die Kommandos werden nacheinander ausgeführt. |
& | Das Programm wird im Hintergrund ausgeführt |
{BEFEHLE} | Führt die Befehle in der aktuellen Shell aus (Vorsicht - falls durch eine UND-/ODER-Verknüpfung die schließende Klammer übersprungen wird, führt das zu irritierenden Fehlermeldungen) |
(BEFEHLE) | Führt die Befehle in einer neuen Shell aus (also werden keine Variablen der aktuellen Shell verändert usw.) |
.
Anführungsstriche
" | doppelte | Variablen und rückwärtige Anführungsstriche (Backticks) werden aufgelöst |
' | einfache | Beides wird nicht aufgelöst |
` | rückwärtige | Der Text wird als Kommando interpretiert und liefert dessen Ausgabe zurück |
.
Strukturen
while
while BEDINGUNG do BEFEHL BEFEHL done
BEDINGUNG ist dabei i.d.R irgendein Programm, das entweder den Exit-Code "0" (=wahr) oder eine einen beliebigen anderen Exit-Code (=falsch) zurückliefert - häufig ist dies das Programm "test"
until
Siehe "while" - einfach "until" stattdessen einsetzen.
for
for 1 2 3 do BEFEHL BEFEHL done
for ((i=1; i<10; i++)) do BEFEHL BEFEHL done
for i in {1..10} do BEFEHL BEFEHL done
if
if BEDINGUNG; then BEFEHL BEFEHL else BEFEHL BEFEHL fi
case
case $VARIABLE in a|b ) BEFEHL BEFEHL ;; c ) BEFEHL ;; * ) BEFEHL ;; esac
Anmerkungen:
- While- und for-Schleifen können durch "break" beendet und durch "continue" mit der nächsten Iteration fortgesetzt werden
- BEDINGUNG ist üblicherweise ein Programm, dessen Exit-Code verwendet wird
- Falls dazu das builtin "test" verwendet wird, ist auch die verkürzte Schreibweise: "![ TEST_PARAMETER ]" möglich (die Leerzeichen zwischen den eckigen Klammern sind wichtig!)
- BEDINGUNG kann auch eine beliebig lange Liste von Kommandos sein - relevant ist nur der Exit-Code des letzten Befehls
- Anstelle der Zeilenumbrüche kann natürlich auch ein Semikolon verwendet werden.
.
String-Funktionen (Variablen-Substitution)
In Abhängigkeit vom Status der Variablen
${var} | die Variable |
${var:-default} | falls die Variable nicht gesetzt ist, wird der default-Wert zurückgegeben |
${var:=default} | falls die Variable nicht gesetzt ist, wird ihr der default-Wert zugewiesen |
${var:+alternative} | falls die Variable gesetzt ist, wird "alternative" zurückgegeben, ansonsten der Wert der Variable (also null (leer)) |
.
Zeichenorientiert
${#var} | die Anzahl der Zeichen in "var" |
${var:offset} | einen Teilstring von "var", beginnend beim Zeichen Nr. "offset" |
${var:offset:length} | liefert "length" Zeichen von "var" beginnend bei Zeichen Nr. "offset" |
${!var} | gibt den Inhalt der Variablen wieder, dessen Name in var steht. |
.
Ersetzungen
${var^^} | wandelt alle Kleinbuchstaben in Großbuchstaben um |
${var,,} | wandelt alle Großbuchstaben in Kleinbuchstaben um |
${var!/-/_} | wandelt den ersten Bindestrich in einen Unterstrich um |
${var!//-/_} | wandelt alle Bindestriche in Unterstriche um |
${var%regexp} | entfernt das kleinstmögliche Suffix, für das "regexp" zutrifft (aus "var=gurgelFressFisch" und "regexp=F.*$" wird somit "gurgelFress" |
${var%%regexp} | entfernt das größtmögliche Suffix, fuer das "regexp" zutrifft (das obige Beispiel ergibt dann "gurgel") |
${var#regexp} | entfernt das kleinstmögliche Präfix |
${var##regexp} | entfernt das größtmögliche Präfix |
$((10#$var)) | entfernt führende Nullen |
.
Gebräuchliche Beispiele
# bar=123
# foo=bar
# echo ${!foo}
123
# foo=bar.sh
# echo ${foo%.sh}
bar
Anmerkung:
- da ein Punkt in einem regulären Ausdruck als beliebiges Zeichen interpretiert wird, würde beispielsweise auch die Endung "zsh" entfernt werden - korrekt wäre also: "${foo%..sh"
.
Berechnungen
- mit "$((formel))" koennen einfache arithmetische Operationen ausgefuehrt werden
- alle Variablen in der Berechnung muessen mit dem ueblichen Dollarzeichen markiert werden (fuer ash erforderlich - fuer bash nicht)
- Variable mit führenden Nullen werden als Oktalzahl interpretiert und sollten mit vorangestellten "10#" maskiert werden, z. B. $((10#$var *3))
- Vorsicht: i=$(($i+1)) und i=$i+1 ist fuer die bash nicht dasselbe - ersteres wird als unendliche Rekursion abgelehnt und letzteres liefert das erwartete Resultat (bei der ash funktioniert nur die erste Variante)
.
Wichtige Shell-Variablen
$1 | $9:: die Parameter beim Skript-Aufruf (koennen mit "shift" weitergeschoben werden, um an die folgenden Parameter zu gelangen) |
$0 | der Name des Shell-Skripts |
$* | alle Parameter der Shell |
$@ | so aehnlich, wie "$*" |
$# | die Anzahl der Parameter |
$? | Exit-Code der letzten Pipeline bzw. des letzten (Vordergrund-)Kommandos |
$! | Exit-Code der letzten Pipeline bzw. des letzten (Hintergrund-)Kommandos |
$$ | die "pid" (Prozessnummer) des Skripts |
.
Funktionen
Eine Funktion kann auf zweierlei Weise deklariert werden:
name () { BEFEHL1 BEFEHL2 }
oder
function name { BEFEHL1 BEFEHL2 }
Anmerkungen:
- "return n" liefert den Exit-Code "n" zurück
- Die Parameter der Funktion sind als "$1" bis "$9" verfuegbar
- Der Aufruf einer Funktion startet einen eigenen Prozess
- Variablen, die nur innerhalb der Funktion gültig sein sollen, müssen bei der ersten Verwendung durch ein vorangestelltestes "local" definiert werden
- Der Aufruf einer Funktion erfolgt einfach durch ihren Namen
.
Der Befehl read
Mit read kann man vom Benutzer Eingaben als String einlesen. Der eingelesene Sting wird standardmäßig in der Variablen $REPLY gespeichert.
$ read meine Eingabe $ echo $REPLY meine Eingabe
Die wichtigsten Optionen sind
-p | Eingabeprompt, z.B.: -p "Press any key" |
-n | Nur eine bestimmte Anzahl Zeichen einlesen, z.B: -n 1 |
-s | Keine Anzeige der Eingabe |
-a | Die Wörter der Eingabe in ein Array speichern, z.B. -a ,myarray |
Beispiele
read -n1 -p "Press any key to continue" | Nach Eingabe eines beliebigen Zeichens wird die Eingabe beendet. |
read -s -p "Passwort: " PASSWD | Die Eingabe wird nicht angezeigt und in der Variablen $PASSWD gespeichert |