Scripten mit Bash

Die Bash-Shell ist unter Linux ein unverzichtbares interaktives Werkzeug. Darüber hinaus kann sie durch Shell-Scripts Aufgaben lösen und Abläufe automatisieren. Dieser kleine Beitrag kann die Möglichkeiten nur andeuten und zur Eigeninitiative motivieren.

Der folgende kleine Crash-Kurs fängt nicht bei null an, sondern setzt grundlegende Kenntnis der interaktiven Shell-Benutzung bereits voraus. Er zeigt Regeln und Kniffe bei der direkten, interaktiven Arbeit im Terminal und legt nachfolgend die Basis für komplexere Shell-Scripts.

Interne und externe Befehle

Die Shell muss bei jedem Befehl unterscheiden, ob es sich um ein externes Programm oder einen internen Befehl der Bash-Shell handelt. So ist „gedit“, also der Texteditor, ebenso ein externes Programm wie „ls“, das Programm zum Auflisten von Dateien. „cd“ hingegen, zuständig für den Verzeichniswechsel, werden Sie auf der Festplatte nicht finden, da es sich um einen internen Bash-Befehl handelt. Solange externe Programme in den Standardpfaden wie /bin und /usr/bin liegen, findet sie die Shell automatisch, sodass Sie sich um die Unterscheidung „intern – extern“ nicht groß kümmern müssen. Dennoch ist es wichtig zu wissen, dass externe Programme eventuell eine komplette Pfadangabe oder den vorherigen Wechsel in ihr Verzeichnis benötigen, um fehlerfrei zu starten.
Aufbau einer Kommandozeile
Eine Kommandozeile besteht aus einem oder mehreren Wörtern, die durch Leerzeichen getrennt sind. Das erste Wort ist immer das maßgebliche interne oder externe Programm. Im simpelsten Fall genügt dieses eine Wort – etwa „firefox“ oder „ls“. Häufig folgen aber weitere Argumente wie etwa Dateiangaben oder auch Optionen des jeweiligen Programms: So nutzt „firefox –safe-mode“ die eingebaute Browser-Option, im abgesicherten Modus ohne Erweiterungen zu starten. Jede derartige Programm-Option muss mit genauester Kenntnis der Schreibweise eingegeben werden, andernfalls wird sie vom Programm bestenfalls ignoriert oder als fehlerhaft bemängelt.
In komplexeren Beispielen kommen zusätzliche Befehlsverkettungen zum Einsatz:
free | awk '/Speicher:/ { print "Speicher frei (%): " int($4/$2*100)}'
Der Grundbefehl „free“ ermittelt die aktuelle Speichersituation – aber relativ unübersichtlich. Der dann folgende (mit den Tasten Alt Gr-| erzeugte) Längstrich ist das fundamentale Signal für Befehlsverkettungen. In diesem Fall wird die unübersichtliche Ausgabe von „free“ an ein zweites Programm „awk“ übergeben, das zunächst die Zeile mit „Speicher:“ herausfiltert und dort wiederum das zweite und vierte Textfeld ($2 und $4). „awk“ kann die gefundenen Angaben auch gleich verrechnen und mit erklärendem Text ausgeben.

0_Syntax-Highlighting
Hilfreich bis unentbehrlich: Ein guter Script-Editor mit Syntax-Highlighting (hier gedit) macht die Arbeit einfacher.

Zwischen interaktiver Nutzung und Scripting

Der Übergang zwischen interaktiver Nutzung und Scripting ist fließend. Sobald Sie beginnen, in der automatisch eingelesenen Standarddatei .bashrc (im Home-Verzeichnis) Änderungen vorzunehmen, sind Sie auf dem Weg zum Scripten. Typische erste Anpassungen in der .bashrc sind Befehlsabkürzungen in Form von Alias-Definitionen. Beachten Sie, dass Änderungen der Datei bashrc immer erst für Terminals gelten, die nach der Änderung gestartet werden. So verhilft etwa dieses Alias
alias d='cd $HOME/Desktop'
dazu, mit der Eingabe „d“ schnell zum Desktop zu wechseln. Achtung: Der Desktop-Ordner heißt bei einigen Linux-Distributionen auch „Schreibtisch“ oder „Arbeitsfläche“.
Folgendes Alias
alias 2d='cp --target-directory=$HOME/Desktop $1'
dient einer schnellen Kopie zum Desktop: Nach der Eingabe „2d“ sollte ein Dateiname folgen, den das Alias mit der Variablen „$1“ verarbeitet und die jeweilige Datei zum Desktop kopiert.
Unentbehrlich ist mittelfristig sicher ein Alias, das die bashrc-Datei in einen Editor lädt:
alias ini='gedit ~/.bashrc & disown'
„& disown“ ist nicht notwendig, sorgt aber dafür, dass der Editor unabhängig vom Terminal gestartet wird. Ein ähnliches einfaches Beispiel ist der Aufruf des grafischen Dateimanagers im aktuellen Verzeichnis:
alias x='nautilus $PWD & disown'
Ein Alias kann aber mehr als nur ein einzeiliges Kommando aufnehmen, wobei Sie die Befehle mit einem Semikolon trennen:
alias env='env | sort;echo "";echo -e ${PATH//:/\\n}'
Nach der Eingabe „env“ werden hier die Umgebungsvariablen sortiert angezeigt und nach einer Leerzeile noch einmal der Systempfad in gut lesbarer Form mit je einem Eintrag pro Zeile ausgegeben.
Ein weiteres Beispiel ermittelt mit „extip“ die externe IP-Adresse, wobei es einen beliebten Internetdienst befragt. Das ebenfalls benutzte Tool Curl ist meist Systemstandard, und ist – wo nicht – schnell nachinstalliert:
alias extip='echo -n "Externe IP = ";curl http://ifconfig.me'
Sobald Aliases mehr als drei, vier Kommandos enthalten und mit den trennenden Strichpunkten schwer lesbar werden, lohnt sich eine andere Form, nämlich die einer Funktion. Auch Funktionen, jedenfalls die allerwichtigsten, können Sie in der .bashrc unterbringen. Die Syntaxbasis einer Funktion sieht wie folgt aus:
function Name() {
[Befehle…]
}

Auch Funktionen sind im einfachsten Fall simple Befehlsstapel, nachfolgend etwa, um einige Systeminformationen abzufragen:
function ver() {
echo -n "System: "; uname -o
echo -n "Kernel: "; uname -r
echo -n "CPU : "; uname -p
echo -n "Name : "; uname -n
gnome-shell --version
}

Ein etwas komplexeres Beispiel zeigt die Timer-Funktion in der Abbildung auf dieser Seite, die eine Stoppuhr auslöst, welche mit beliebiger Taste beendet wird. Die gemessene Zeitdauer wird dann am Prompt exakt angezeigt.
Die weitere Beispielfunktion „xd“ eignet sich ebenfalls für die Datei-bashrc, weil sie den interaktiven Komfort der Kommandozeile erhöht: Sie macht bereits von einfachen Kontrollstrukturen Gebrauch, in diesem Fall von einer einfachen Fallunterscheidung mit If – Then – Else. Beim Verzeichniswechsel mit „xd“ spielt es keine Rolle, ob ein Ordnerpfad oder ein Dateipfad nachfolgt, denn im zweiten Fall zieht sich die Funktion einfach den Ordner aus dem Dateipfad. Das ist etwa praktisch, wenn Sie Dateiobjekt aus dem Dateimanager in das Terminal ziehen und dann in dessen Ordner wechseln wollen. Das der Funktion nachgestellte Alias „cd=‘xd‘“ macht das intelligentere „xd“ zum Standard.

Einfache Stoppuhr: Die Funktion „timer“ speichert die Startzeit. Nach beliebigem Tastendruck errechnet sie die exakte Differenz zur Endzeit und zeigt das Ergebnis an.
Einfache Stoppuhr: Die Funktion „timer“ speichert die Startzeit. Nach beliebigem Tastendruck errechnet sie die exakte Differenz zur Endzeit und zeigt das Ergebnis an.

Selbständige Shell-Scripts

Die bisher genannten Aliases der Datei bashrc oder auch Funktionen innerhalb dieser Datei bedeuten zwar bereits Scripting, waren aber immer noch auf den interaktiven Einsatz am Terminal-Prompt ausgerichtet. Es gibt aber gute Gründe, ein Bash-Script als unabhängige und selbständige Datei abzulegen: Nur so ist es möglich, ein Script per Mausklick aus der grafischen Oberfläche zu starten oder automatisch beim Logon sowie als periodischen Cron-Job auszuführen. Außerdem ist es nicht sinnvoll, umfangreiche, aber nur gelegentlich genutzte Scripts allesamt stets in der bashrc-Datei mitzuschleppen.
Bash-Scripts sind wie die bereits genutzte bashrc einfache Textdateien, die Sie mit einem Editor Ihrer Wahl erstellen und bearbeiten. Der Editor sollte aber Fähigkeiten wie Syntax-Highlighting mitbringen, was die Übersicht wesentlich verbessert. Der empfehlenswerte Editor gedit zeigt unter „Ansicht -> Hervorhebungsmodus -> Skripte“ die Option „sh“, die Sie für Shell-Scripts wählen sollten. Auch die Farbschemata unter „Bearbeiten -> Einstellungen -> Schrift und Farben“ sind beim Scripten wichtiger als beim Tippen von Text.
Ein Bash-Script beginnt immer mit dieser Zeile:
#!/bin/bash
Das Zeichen „#“ leitet eigentlich einen Kommentar ein, also eine Zeile, die die Shell ignorieren soll. In der Kombination „#!“ gibt sie jedoch die zu verwendende Shell an. Da nicht auf allen Systemen die Bash-Shell Standard sein muss, erfährt das System hier, welches Programm es starten soll. Das setzt natürlich voraus, dass /bin/bash tatsächlich existiert, was aber in aller Regel der Fall ist.
Für einen ersten Versuch erstellen Sie ein ganz kurzes Script test.sh mit folgendem Inhalt:
#!/bin/bash
for i in *.*
do mv $i $(echo $i | tr ':' '-')
done

Das Beispiel-Script ersetzt bei allen Dateien des aktuellen Verzeichnisses eventuelle Doppelpunkte im Namen durch Bindestriche. Das ist eine nützliche Hilfe, wenn Sie Dateien von Linux nach Windows kopieren wollen. Für eine vollständigere Ersetzung problematischer Zeichen müssen Sie nur die dritte Script-Zeile für weitere erforderliche Zeichen wiederholen.
Ein guter Ort für solche speziellere Scripts ist etwa ein Ordner /Scripts im Home-Verzeichnis. Öffnen Sie dann ein Terminal-Fenster, und geben Sie
chmod u+x ~/Scripts/test.sh
ein, um das Script für den aktuellen Benutzer „Ausführbar“ zu machen. Alternativ geht das auch im Dateimanager über „Eigenschaften -> Zugriffsrechte -> Datei als Programm ausführen“. Gehen Sie dann mit „cd“ in einen Ordner, der Dateien mit problematischen Zeichen enthält, und starten Sie mit
sh ~/Scripts/test.sh
das Script. Die Variable „$i“ nimmt innerhalb der do-Schleife nacheinander jeweils den Wert des nächsten Dateinamens im aktuellen Verzeichnis an. „echo $i“ übergibt den Namen an das Translate-Tool „tr“.
Ein weiteres einfaches Script verbessert die Benutzung des gnome-search-tools, indem es einen übergebenen Suchbegriff und das aktuelle Verzeichnis direkt an das Tool weitergibt. Wird das Script an der grafischen Oberfläche oder auch im Terminal ohne Suchbegriff gestartet (also ohne Parameter $1), dann lädt das Tool ganz normal mit dem Home-Verzeichnis als Vorgabe. Eine typische Eingabe am Prompt wäre dann:
sh ~/Scripts/search Shakespeare
Den Aufrufkomfort externer Shell-Scripts sollten Sie bei allen wichtigeren Exemplaren durch Aliases weiter und deutlich verbessern. Mit einem Alias „search=sh ~/Scripts/search‘‘ verkürzt sich die Eingabe auf
search Shakespeare
Script-Start per GUI: Wenn Sie Shell-Scripts über die grafische Oberfläche per Doppelklick starten wollen, genügt es nicht, den Scripts das Attribut „Ausführbar“ zuzuweisen. Zusätzlich müssen Sie diese Startoption im Dconf-Editor oder – meist einfacher – im Dateimanager erlauben. Bei Nautilus (Ubuntu) und Nemo (Mint) finden Sie die Option unter „Bearbeiten -> Einstellungen -> Verhalten -> Ausführbare Textdateien“.

Suchbegriff und aktuellen Pfad direkt an das grafische Gnome-Search-Tool übergeben: Wird das Script ohne Parameter aufgerufen, setzt das Script lediglich den Standardpfad /home.
Suchbegriff und aktuellen Pfad direkt an das grafische Gnome-Search-Tool übergeben: Wird das Script ohne Parameter aufgerufen, setzt das Script lediglich den Standardpfad /home.

Wichtige Tipps zur Ablaufkontrolle

Einige kleinere IF-Entscheidungen kamen bereits zu Wort. Es gibt zahlreiche Parameter und Operatoren für IF-Fallunterscheidungen. Ein wichtiger Parameter „-e“
if [ -e ~/Schreibtisch ]; then
cd ~/Schreibtisch
fi

überprüft die Existenz eines Dateiobjekts. Der Parameter „-n“ stellt fest, ob die nachfolgend genannte Variable existiert („not empty“ ist):
if [ -n “$var“ ]; then echo “Existiert“; fi
Dies ist auch von besonderer Bedeutung, wenn Sie die Übergabeparameter an ein Script („$1“, „$2“ etc.) auswerten müssen. Viele weitere Vergleich-Operatoren müssen Sie bei Bedarf im Web recherchieren. Wichtig zu wissen ist es, dass die Operatoren „-eq“ (gleich), „-ne“ (ungleich) nur bei Integer-Zahlen funktionieren, während Sie für den Textvergleich „=“ (gleich) und „!=“ (ungleich) verwenden müssen:
if [ “X“ = “Y“ ]…
Diese Zeile ist ebenso korrekt wie folgende:
if [ “21“ –eq “5“ ]…
Schleifenkonstruktionen mit While, Until (meist entbehrlich) und For haben entweder eine genau bezifferbare Anzahl der Durchläufe oder warten auf ein bestimmtes Ereignis (etwa ein Textinhalt, Dateiname), das die Schleife beendet. Eindeutig bezifferbar, ohne dass Sie das selbst absehen oder programmieren müssten, ist etwa die Anzahl der Dateien in folgender Schleife:
for dat in `find /home -name "*.png" -type f`
do echo $dat
done

Dasselbe gilt für das Einlesen und Analysieren einer Textdatei mit
while read line; …
bis zum Ende der Datei. In eher selteneren Fällen sind genau bezifferbare Schleifendurchläufe folgender Sorte die richtige Wahl:
for i in {1..255}; do ping –c1 192.168.0.$i; done
Viel häufiger ist es erforderlich, in der Schleife bei jedem Durchlauf eine Variable zu neu aktualisieren, die dann bei einem bestimmten Wert die Schleife stoppt:
while [ „$name“ != „LinuxWelt“ ]; …
Noch pragmatischer kann es sein, eine Schleife etwa mit „while :“ ohne Bedingung endlos laufen zu lassen und beim entscheidenden Ereignis direkt im Schleifencode mit dem Schlüsselwort „break“ abzuspringen.

Basis-Script für eine Textdatei-Auswertung: Die While-Schleife liest Zeile für Zeile einer übergebenen Datei ein und zeigt sie in diesem Fall lediglich mit Zeilenummer an.
Basis-Script für eine Textdatei-Auswertung: Die While-Schleife liest Zeile für Zeile einer übergebenen Datei ein und zeigt sie in diesem Fall lediglich mit Zeilenummer an.

————————————————————————–
Empfohlene Quellen für Bash und Scripting

GNU-Shell Bash (deutsch): www-user.tu-chemnitz.de/~hot/unix_linux_werkzeugkasten/bash.html

Shell-Programmierung (deutsch): http://openbook.galileocomputing.de/shell_programmierung/

Advanced Bash-Scripting Guide (englisch): http://tldp.org/LDP/abs/html/index.html

————————————————————————–

Anhang 1: Informativer und cooler Prompt

Manche fundamentale Info müssen Sie gar nicht erst explizit erfragen, sondern können sich diese standardmäßig am Terminal-Prompt anzeigen lassen. Möglich ist hier im Prinzip alles, weil die Bash-Shell mit dem „PROMPT_COMMAND“ eine interne Funktion vorsieht, die vor jeder Prompt-Darstellung aufgerufen wird. Daher können Sie in einer selbstgestrickten Funktion „promptcmd“ alles abfragen, was Sie Echtzeit-aktuell am Prompt angezeigt haben wollen. Die Abbildung zeigt den passenden Abschnitt in der Datei bashrc und darunter das Ergebnis, wie es sich im Terminal auswirkt.

Cooler-Prompt

Anhang 2: Grafische Dialoge für Bash-Scripts

In verbreiteten Distributionen wie Ubuntu und Linux Mint ist ein Programm für grafische Dialoge in der Regel vorinstalliert. Geben Sie am Terminal den Befehl dialog ein, um sich dieser Tatsache zu versichern. Sie erhalten eine Reihe von Unterfunktionen angezeigt wie „inputbox“, „msgbox“ oder „yesno“. Den ganz großen Charme versprühen diese Dialoge nicht, aber gelegentlich ist der Mausklick doch angenehmer als eine Texteingabe (mit „read“). Das im nachfolgenden Bild abgebildete Beispiel soll einige Möglichkeiten andeuten: In der Inputbox wird ein Name abgefragt, danach folgt eine Yes-No-Entscheidung, deren Ergebnis schließlich mit einer Notify-Meldung quittiert wird.

GUI