Dieses Kapitel vermittelt eine Einführung in die Kommandozeile (Command Line Interface, CLI) und die Programmierung von Shell-Skripten. Weiterführende Informationen und Tutorials unterschiedlichen Umfangs finden sich z. B. im Bash-Programming-Intro-HOWTO, dem Bash-Beginners-Guide und Advanced Bash-Scripting Guide.

Kommandozeile

Bearbeiten

Dokumentation und Hilfe

Bearbeiten

Zu jedem Befehl/Kommando kann man unterschiedlich detailierte Informationen, Erklärungen und Beispiele abrufen. Die folgenden Beispiele verwenden exemplarisch sich selbst als Argument, normalerweise gibt man hier ein beliebiges anderes Kommando (oder auch Objekt, vergl. man interfaces) an, für das man sich interessiert. Man beachte insbesondere man, die „manual pages“. Die GNU Info Seiten müssen oft extra installiert werden (apt install info), um verfügbar zu sein.

whatis whatis   # durchsucht die Indexdatenbank nach Kurzbeschreibungen
man man         # ein Interface für die System-Referenzhandbücher
help help       # Information über Kommandos, die innerhalb der Shell selbst implementiert sind 
info info       # GNU Info Seiten 
apropos apropos # suche in Handbuchseiten und deren Kurzbeschreibungen

whatis --usage ; man --help ; info -h

Dateibaum, Verzeichnisse und Dateien

Bearbeiten
ls -l → liste Inhalt eines Verzeichnisses auf cd → wechsele Verzeichnis tree → Dateibaum
touch → lege Datei an bzw. ändere Zeistempel rm → lösche Datei rm -r → lösche rekursiv Verzeichnis und Dateien
cp → Datei kopieren cp -r → rekursiv kopieren mv → umbenennen, verschieben
mkdir → lege Verzeichnis an mkdir -p → lege Verzeichnisse an rmdir → lösche leeres Verzeichnis
cat → Datei über stdout ausgeben more → Datei stückweise betrachten lessless is more!
chown → Besitzer („user“) einer Datei ändern chgrp → Gruppe („group“) einer Datei ändern chmod → Zugriffsrechte bearbeiten
stat → Meta-Daten einer Datei ln, ln -s → Hard- bzw. Softlink

Ressourcen

Bearbeiten
free -h → Arbeitsspeicher anzeigen df -h → Plattenplatz anzeigen top → Auslastung live!
uptime → der Name ist Programm! uname -a → Info über Kernel fdisk, gdisk → Partitionen anzeigen
lscpu → Info über CPU(s) /proc/cpuinfo → Info über CPU(s) lspci → Info über PCI-Bus und -Geräte
lsusb → Info über USB-Bus und -Geräte lsmod → Kernelmodule auflisten journalctl -b → System Log Messages
systemctl status → System Status (systemd) ps → Prozesse auflisten pstree → Prozess-Baum
kill → Signale an Prozess senden dudisk usage (Plattenplatz) dmidecode → BIOS- und Harware-Info

Netzwerk

Bearbeiten

Werkzeuge

Bearbeiten
find → Dateisystem durchsuchen grep → Datei nach Muster durchsuchen kill → Signale an Prozess senden
sort → sortieren cut → „zerschneiden“ tar → Archivierung
hexdump, xxd → binärer Dateiinhalt sedstream editor, Filter wcword count
head → Datei-Anfang tail → Datei-Ende trtranslate (or delete)

Ein-/Ausgabe Umlenkung

Bearbeiten

| Pipe-Symbol: Ausgabe (stdout) wird an weiteres Programm weitergeleitet: cmd1 | cmd2
cmd < datei stdin wir aus datei gelesen
cmd > datei stdout wird in datei geschrieben
cmd >> datei stdout wird an datei angehängt
cmd 2> datei stderr wird in datei geschrieben
cmd 2>&1 stderr wird an die gleiche Stelle wie stdout geschrieben

Shell-Skripte

Bearbeiten

Allgemeiner Aufbau

Bearbeiten

Ein Skript im allgemeinen bzw. ein Shell-Skript im speziellen bezeichnet ein in einer Skriptsprache verfasstes Programm. Skriptsprachen werden meist durch einen Interpreter ausgeführt und finden hauptsächlich Verwendung beim Prototyping sowie in der Systemadministration (Konfiguration, Automatisierung, Orchestrierung, ….)

Programm-Header

Wir betrachten hier nur die unter GNU/Linux und anderen unixoiden Betriebssystemen üblichen Skripte. Die erste Zeile eines Skripts, genannt Shebang, definiert den zur Ausführung benötigten Interpreter und hat den folgenden Aufbau: #!/usr/bin/sh. Nach dem #!, interpretierbar als Magic Number (vergleiche echo '#!' | xxd), folgt der Pfad des Interpreterprogramms: /usr/bin/sh. Beispiele sind:

Nach dem Shebang folgt üblicherweise der (nur) menschenlesbare Teil des Programms: Urheber, Lizenz- und anderweitige Informationen von Interesse. Diese Zeilen fangen mit einem Kommentarzeichen # an. Grundsätzlich wird alles in einer Zeile nach dem # vom Interpreter ignoriert, wobei die erste Zeile mit dem Shebang eine Ausnahme bildet.

Speziell bei Shellskripten empfiehlt sich gleich zu Beginn des eigentlichen Programm-Codes noch die Anweisung set -eu. Mit set -e bricht das Shellskript ab, wenn ein Fehler auftritt (Rückgabewert einer Anweisung ≠0). Mit set -u werden unbelegte Variablen als Fehler reklamiert. Beides hilft bei der Entwicklung, Fehlern auf die Spur zu kommen. Details und weitere Informationen findet man z. B. mit man bash im Absatz SHELL BUILTIN COMMANDS.

Im einfachsten Fall ist ein Shell-Skript einfach eine Reihe von Befehlen, die nacheinander ausgeführt werden. Mit Variablen und den ebenfalls weiter unten behandelten Kontrollstrukturen kann man aber beliebige Programme schreiben. Die Shell-Programmierung reiht sich damit in die stetig wachsende Menge der verschiedenen Programmiersprachen ein, wobei jede ihre Vor- und Nachteile, gut oder weniger gut geeignete Problemstellungen und Anwendungsbereiche hat.

Um ein Shell-Skript letztlich ausführen zu können, muss es ausführbar (executable) gemacht werden:
chmod +x shellscript.sh

Man führt es dann mit Pfadangabe aus:

./shellscript.sh
pfad/zum/shellscript.sh

Durch die Ausführung mit Pfadangabe wird verhindert, dass ein im Arbeitsverzeichnis liegendes, möglicherweise gar nicht bemerktes Skript anstelle des erwarteten Programms im Pfad (vergleiche echo $PATH) ausgeführt wird.

Variablen

Bearbeiten

In Shell-Skripten spielen selbst definierte genauso wie automatisch vorhandene Variablen eine große Rolle. Man definiert und arbeitet mit Variablen folgendermaßen:

#!/usr/bin/sh
# Demonstriert die einfache Verwendung von Variablen
set -eu

GRUSS="Hallo"
FRAGE="wie geht es Dir?"
ZAHL=10 

echo "Ein Gruß: $GRUSS"
echo 'Kein Gruß: $GRUSS' # Zwischen "" werden Variablen referenziert, zwischen '' nicht!
echo "${GRUSS}le, $FRAGE" # mit {} kann ein Variablenname abgegrenzt werden.
echo "$ZAHL + 5 ist $((ZAHL + 5))"
Vordefinierte Variablen

Eine Reihe von Variablen ist vordefiniert und kann nicht gesetzt werden:

#!/usr/bin/bash
# Demonstriert vordefinierte Variablen. Aufruf mit mindestens zwei beliebigen Parametern.
set -eu
echo "Der erste Parameter des Programms: '$1', und der zweite: '$2', usw …"
echo "Alle übergebenen Parameter des Programms: '$@' oder auch '$*'."
echo -e "Wie man sieht wurden $# Parameter übergeben.\n"

echo "Dieses Programm wurde als '$0' aufgerufen und läuft unter der Prozess-ID $$."
sleep 10 & # das &-Zeichen führt den Befehl im	Hintergrund aus.
echo "Die Prozess-ID des 'sleep'-Befehls ist $!."
ps
echo -e "Stimmt's?\nRückgabewert des letzten Programms bzw. Kommandos: '$?'.\n"

set +e # damit kein Abbruch erfolgt
kommandogibtsnicht
echo -e "Rückgabewert des letzten Programms bzw. Kommandos: '$?'.\n"
set -e # Abbruch bei Fehler wieder eingeschaltet.

echo "Gib jetzt in der Shell ein: 'echo \$?'.  Woher kommt die Zahl?"
exit 123

Kontrollstrukturen

Bearbeiten

Bedingungen und Kontroll-Operatoren

Bearbeiten

Das Durchlaufen von Kontrollstrukturen erfolgt abhängig von Bedingungen (Tests). Bedingungen sind entweder wahr (Rückgabewert = 0) oder falsch (Rückgabewert ≠ 0). Statt test AUSDRUCK kann man auch [ AUSDRUCK ] schreiben. In vielen Shells gibt es eine erweiterte und verbesserte Variante (siehe man bash), die statt [ AUSDRUCK ] zwei Klammern verwendet: [[ AUSDRUCK ]]. Hier ein paar wenige Beispiele, für weitere konsultiere man man test:

[[ $STRING1 = $STRING2 ]]
[[ $INTEGER1 -eq $INTEGER2 ]]
[[ -e $FILE ]]

Die Kontroll-Operatoren && und || erlauben die Ausführung eines Ausdrucks abhängig vom Rückgabewert des zuvor ausgeführten:

AUSDRUCK1 && AUSDRUCK2      ## AUSDRUCK2 wird nur ausgeführt, wenn AUSDRUCK1 „wahr“=0 zurückgibt
AUSDRUCK1 || AUSDRUCK2      ## AUSDRUCK2 wird nur ausgeführt, wenn AUSDRUCK1 „falsch“≠0 zurückgibt
## Der Rückgabewert eines Ausrucks wird in der Variable $? gespeichert: 
/usr/bin/true; echo $?
/usr/bin/false; echo $?

Verzweigung

Bearbeiten
echo -n "Input a Number: "
read N
if [ $N -gt 0 ] ; then
  echo "$N is greater than 0, a positive number." 
elif [ $N -lt 0 ] ; then
  echo "$N is less than 0, negative number." 
else
  echo "$N must be exactly zero."
fi

Fallunterscheidung[1]

Bearbeiten
SPACE=$(df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -)

case $SPACE in
    [1-6]*)
        MESSAGE="All is quiet."
        ;;
    [7-8]*)
        MESSAGE="Start thinking about cleaning out some stuff.  There's a partition that is $SPACE % full."
        ;;
    9[1-8])
        MESSAGE="Better hurry with that new disk …  One partition is $SPACE % full."
        ;;
    99)
        MESSAGE="I'm drowning here!  There's a partition at $SPACE %!"
        ;;
    *)
        MESSAGE="I seem to be running with an nonexistent amount of disk space …."
        ;;
esac
echo "$MESSAGE"

while-Schleife

Bearbeiten
N=0
while [ $N -lt 10 ] ; do
  echo "$N"
  N=$((N+1))
done

until-Schleife

Bearbeiten
N=0
until [ $N -gt 10 ] ; do
  echo "$N"
  N=$((N+1))
done

for-Schleife

Bearbeiten
LIST="a b c"
for ITEM in $LIST ; do echo "$ITEM" ; done

for N in {-5..10} ; do
  echo "\$N hat den Wert: $N"
done

LIST=$(seq 0 10 50)
for ITEM in $LIST ; do
  echo "$ITEM"
done

Sonstige Hilfen und Werkzeuge

Bearbeiten

Das Programm shellcheck eignet sich zum Prüfen von Shell-Code und gibt viele Hinweise und Verbesserungsvorschläge.

The Linux Documentation Project (LDP): Advanced Bash-Scripting Guide



  1. In Anlehnung an ein Beispiel aus dem Bash-Beginners-Guide

Aufgaben

Bearbeiten

Inventory Skript

Bearbeiten

Multiping Skript

Bearbeiten