Dein erstes Bash-Skript
Der Aufbau eines Bash-Skripts: Shebang, echo, Kommentare, Nutzereingabe und die Grundlagen von Variablen und Quoting.
Inhaltsverzeichnis
Dein erstes Bash-Skript
Zeit, ein paar nuetzliche Skripte zu bauen. Wir starten klein und erweitern.
Hallo Welt
hallo.sh:
#!/usr/bin/env bash
echo "Hallo, Bash!"
Ausfuehrbar machen und starten:
chmod +x hallo.sh
./hallo.sh
# Hallo, Bash!
Der Shebang erklaert
#!/usr/bin/env bash
#!(Shebang) - sagt dem Kernel: โInterpretiere die Datei mit folgendem Programmโ/usr/bin/env bash- suchtbashim PATH und startet es. Portabler als#!/bin/bash.
Ohne Shebang muesstest du immer bash hallo.sh aufrufen.
Kommentare
# Einzeiliger Kommentar
# Alles nach # bis Zeilenende wird ignoriert
Bash hat keinen mehrzeiligen Kommentar. Fuer groessere Bloecke nutzt man oft:
: '
Hier ein mehrzeiliger
"Kommentar" - technisch ein No-Op mit einem String
'
echo und printf
echo "Hallo, Bash!"
echo # leere Zeile
echo "mit Zeilenumbruch"
echo -n "ohne Zeilenumbruch"
Fuer formatierte Ausgabe ist printf besser:
printf 'Name: %s\n' "Anna"
printf 'Alter: %d\n' 28
printf '%-10s %s\n' 'Name:' 'Anna' # linksbuendig, Breite 10
printf ist portabler und vorhersehbarer als echo. Fuer produktions-Skripte empfohlen.
Variablen
#!/usr/bin/env bash
name="Anna"
alter=28
echo "Name: $name"
echo "Alter: $alter"
echo "Hallo, $name!"
Regeln:
- Kein Leerzeichen um
=!name = "Anna"ist ein Fehler. - Gross-/Kleinschreibung ist signifikant
- Konvention:
snake_casefuer eigene,UPPER_CASEfuer Konstanten / Environment
Variablen-Expansion
name="Anna"
echo "Hallo, $name!" # Hallo, Anna!
echo "Hallo, ${name}!" # genauso, aber klarer
echo 'Hallo, $name!' # Hallo, $name! (single quotes!)
echo "Ordner: ${HOME}/projekte" # interpoliert $HOME
Merke:
"..."(double-quoted): Variablen und$( )werden expandiert'...'(single-quoted): nichts wird expandiert - literal${var}: eindeutige Syntax, wenn danach Buchstaben folgen
Kommando-Substitution
Ergebnis eines Befehls in eine Variable packen:
heute=$(date +%Y-%m-%d)
echo "Heute ist $heute"
nutzer=$(whoami)
echo "Hallo, $nutzer!"
Alternative (aelter): Backticks `...` - aber $(...) ist besser (verschachtelbar).
Nutzereingabe
#!/usr/bin/env bash
read -p "Wie heisst du? " name
echo "Hallo, $name!"
read var- liest eine Zeile invar-p "Prompt"- zeigt einen Prompt
Zahlen einlesen
read -p "Dein Alter: " alter
if [[ "$alter" =~ ^[0-9]+$ ]]; then
echo "Alter ist $alter"
else
echo "Das war keine Zahl" >&2
exit 1
fi
Das [[ "$alter" =~ ^[0-9]+$ ]] ist ein Regex-Match.
Mehrere Werte
read -p "Name und Alter: " name alter
echo "$name ist $alter"
Exit-Codes
Jeder Befehl liefert einen Exit-Code:
0= Erfolg- Alles andere = Fehler
echo "OK"
echo "Exit-Code des letzten Kommandos: $?"
Ein Skript kann selbst einen Exit-Code zurueckgeben:
if [[ ! -f daten.txt ]]; then
echo "Datei fehlt" >&2
exit 1
fi
# ... Hauptprogramm
exit 0
STDOUT und STDERR
echo "Info" >&1 # STDOUT (default)
echo "Fehler" >&2 # STDERR
Warum die Trennung? Damit du Ausgaben verschieden behandeln kannst:
./skript.sh > ausgabe.txt 2> fehler.txt
./skript.sh &> alles.txt # beides in eine Datei
set -euo pipefail - die goldene Regel
Setze diese Zeile an den Anfang fast jedes Skripts:
#!/usr/bin/env bash
set -euo pipefail
Was tun die Flags?
-e: Bei Fehler sofort abbrechen (kein stilles Weiterlaufen)-u: Nicht gesetzte Variable = Fehler (verhindert Tippfehler)-o pipefail: Auch Fehler in der Mitte von Pipes erkennen
Ohne die Flags laufen Skripte oft weiter, auch wenn sie eigentlich scheitern - das verschleiert Bugs.
Ein kleines Beispielskript
begruessung.sh:
#!/usr/bin/env bash
set -euo pipefail
# Pruefen, ob ein Argument uebergeben wurde
if [[ $# -lt 1 ]]; then
echo "Aufruf: $0 <name>" >&2
exit 1
fi
name="$1"
heute=$(date +%A)
echo "Hallo, $name!"
echo "Heute ist $heute."
echo "Tipp: Heute waere ein guter Tag fuer eine Pause."
Ausfuehren:
./begruessung.sh Anna
# Hallo, Anna!
# Heute ist Donnerstag.
# Tipp: Heute waere ein guter Tag fuer eine Pause.
Was ist hier passiert?
$0= Name des Skripts$1= erstes Argument$#= Anzahl der Argumente$(date +%A)= aktueller Wochentag (langer Name)
Kommandozeilenargumente
#!/usr/bin/env bash
echo "Skript: $0"
echo "Anzahl Args: $#"
echo "Alle Args: $@"
echo "Erstes: $1"
echo "Zweites: $2"
# Iteration
for arg in "$@"; do
echo " - $arg"
done
Immer "$@" (mit Quotes!) nutzen - das behandelt Leerzeichen in Argumenten korrekt.
Praktisches Beispiel: Backup-Skript
#!/usr/bin/env bash
set -euo pipefail
QUELLE="${1:-$HOME/projekte}" # Default, wenn $1 fehlt
ZIEL_ORDNER="${HOME}/backups"
ZEITSTEMPEL=$(date +%Y%m%d-%H%M%S)
ZIEL="${ZIEL_ORDNER}/projekte-${ZEITSTEMPEL}.tar.gz"
if [[ ! -d "$QUELLE" ]]; then
echo "Quelle nicht gefunden: $QUELLE" >&2
exit 1
fi
mkdir -p "$ZIEL_ORDNER"
echo "Erstelle Backup..."
tar -czf "$ZIEL" "$QUELLE"
groesse=$(du -h "$ZIEL" | cut -f1)
echo "Fertig: $ZIEL ($groesse)"
Das ist ein echtes, nuetzliches Skript in ~20 Zeilen.
Zusammenfassung
#!/usr/bin/env bash+chmod +x= ausfuehrbares Skript- Variablen ohne Typ, kein Leerzeichen um
= "..."fuer Interpolation,'...'fuer literal$(...)fuer Kommando-Substitutionread -p "..."fuer Eingabe,echo/printffuer Ausgabe- STDERR mit
>&2, Exit-Code mitexit N set -euo pipefailfast immer am Anfang
Im naechsten Kapitel: Variablen, Parameter-Expansion und die Tricks, die Bash maechtig machen.