|
Bevor man sich mit der Programmierung unter einer Shell, hier der Bourne Again Shell, beschäftigt, muss man sich mit einigen Funktionsweisen der ausführenden Umgebung vertraut machen. Dies erspart einem unerwartete Effekte bei der Ausführung von Programmen.
Zu Beginn sollte grklärt werden, was die Shell nicht ist und was man mit ihr nicht machen kann oder sollte:
- Die Shell ist keine Entwicklungsumgebung die einen Komfort von IDE's wie Visual Studio, JBuilder, etc. liefert.
- Mit Shell-Scripting sollte man keine umfangreichen Programme schreiben, sondern eben nur Scripte.
- Mit Shell-Scripting kann man keine grafischen Oberflächen erstellen oder gar Treiber schreiben.
Die Shell ist primär eine interaktive Kommunikationsschnittstelle für den Benutzer zum Betriebssystem. Der Benutzer kann durch sie Kommandos an das Betriebssystem absenden, Informationen abfragen oder eben auch Scripte schreiben, die vielfältigste Aufgaben automatisieren können.
Manchmal kann ein Kommando nur durch die Eingabe des Befehls selbst ausgeführt werden. In der Regel sind jedoch weitere Parameter nötig. Dabei unterscheidet man Parameter in Optionen und Argumente:
- Optionen - Im Befehl integrierte Möglichkeiten zur Manipulation der Arbeitsweise und Ausgabe des Befehls
- Argumente - Angaben (Ziele), auf die sich der Befehl beziehen soll
Zur Unterscheidung zwischen Befehl, Optionen und Argumenten, verwendet die Shell das Leerzeichen (mindestens eins; alternativ den Tabulatorschritt; bei interaktiven Scripts den Zeilenvorschub) als Inter Field Separator (IFS; Worttrenner). Daraus ergibt sich folgender Aufbau eines Kommandos:

IFS IFS | | ls -la /etc | | Argument | | Optionen || Befehl
Oft werden Optionen durch den Bindestrich ( - ) eingeleitet. Sehr oft erlauben Befehle auch mehrere Optionen und manchmal auch mehrere Argumente. Um die genaue Syntax zu ermitteln, ist dann ein Blick in die Hilfedatei ratsam oder man gibt den Befehl einfach ohne Optionen und Argumente ein. In der Regel erhält man dann als Ausgabe eine Kurzbeschreibung des Kommandoaufbaus und der wesentlichen Optionen.
Dadurch, dass Befehle nun oft mehrere Optionen und Argumente akzeptieren und, dass man mittels Sonderzeichen verketten, pipen, unsw. kann, muss die Shell vor der Ausführung die komplette Eingabe analysieren:
- Bis zum ersten Kommandotrenner erfassen und feststellen, ob Umleitungen stattfinden müssen
- Erfassen der Parameter durch die IFS
- Variablen und substituierte Kommandos durch deren Inhalte, bzw. Ergebnisse ersetzen (falls nötig)
- Dateiumlenkungen vorbereiten (falls nötig)
- Wildcards oder Reguläre Ausdrücke auswerten (falls nötig)
- Ausführung des Kommandos
Die Punkte 3 bis 5 sind optional und werden natürlich nur durchgeführt, falls dies auch erforderlich ist. Was diese 3 Punkte genau bedeuten, wird in diesem und weiteren Beiträgen in dieser Kategorie genauer erläutert.
Dass die Shell Befehle annehmen und ebenso Rückmeldungen auf den Bildschirm liefern kann, ermöglichen 3 E/A-Kanäle:
- stdin (Kanalnummer
0; Anwendung: 0< )
- stdout (Kanalnummer
1; Anwendung: 1> )
- stderr (Kanalnummer
2; Anwendung: 2> )

Alle Eingaben des Benutzers gehen an den Kanal stdin (Standard Input; Quelle: Tastatur) und werden verarbeitet. Ausgaben erfolgen an den Kanal stdout (Standard Output; Ziel: Bildschirm). Alle Fehlermeldungen gehen an den Kanal stderr (Standard Error; Ziel: Bildschrim).
Somit ergeben sich Möglichkeiten der Dateiumlenkung und diese sollen an kleinen Beispielen verdeutlicht werden. Zu Beginn wird der Kanal stdout in eine Datei umgelenkt.

Nun liefert der Bildschirm nur noch einen blinkenden Cursor und erwartet Benutzereingaben...
Hallo Besucher! Das ist ein Test zur Umlenkung von Datenströmen. Viel Spaß noch mit diesem Thema.
Man beendet die Eingabe schließlich in einer neuen Zeile und betätigt die Tastenkombination Strg+D.
Damit wurden die Benutzereingaben direkt an die Datei textdatei umgelenkt und dort gesichert. Zur Verifizierung wird der Inhalt nun wieder aus der Datei textdatei ausgelesen, in dem der Eingabekanal auf die Datei umgelenkt wird.

Hallo Besucher! Das ist ein Test zur Umlenkung von Datenströmen. Viel Spaß noch mit diesem Thema.
Bei stdin und stdout ist die Angabe der Kanalnummer allgemein nicht nötig. Das heist, cat < textdatei und cat > textdatei wären ebenso gültige Schreibweisen. Bedingt durch diese Vereinfachung ist Angabe der Kanalnummer 2 bei stderr allerdings immer notwendig.
Aber wozu braucht man nun stderr? In der Shell ist es üblich, dass bei einem erfolgreich durchgeführten Befehl keine gesonderte Erfolgsmeldung zurückgegeben wird. Nur wenn es zu einem Fehler kommt wird der Benutzer informiert. Dies muss aber nicht immer erwünscht sein. Beispielsweise möchte man Fehlermeldungen gerne in einer Datei ablegen. Ein Beispiel:
ls datei_die_nicht_existiert
ls: cannot access datei_die_nicht_existiert: no such file or directory
Eine zu erwartende Fehlermeldung, die standardmäßig am Bildschirm ausgegeben wird. Wenn diese Fehlermeldung nun in einer Datei festgehalten werden soll, reicht es jedoch nicht stdout umzulenken, da die Fehlermeldung nicht von stdout kommt, sondern von stderr. Folglich muss man zur Umsetzung auch den Fehlerkanal stderr umleiten.

ls datei_die_nicht_existiert 2> datei_errorlog
Damit wird die eben noch am Bildschirm ausgegebene Fehlermeldung nur noch in die Datei datei_errorlog geschrieben.
Möchte man nun alle Meldungen umlenken, dann ist dies der Zeichenfolge 2>&1 realisierbar. Diese Zeichenfolge bedeutet: "Lenke stderr genau dorthin, wohin stdout umgelenkt wurde." Im Folgenden sollen alle Meldungen in eine Datei geschrieben werden.

(echo 'Eine umgelenkte Bildschirmausgabe.' && ls datei_existiert_nicht) > datei_log 2>&1
...und wieder verifizieren...
Eine umgelenkte Bildschirmausgabe. ls: cannot access datei_existiert_nicht: No such file or directory
Eine weitere wichtige Grundlage zur Shellprogrammierung ist die Kenntnis über das Verhalten von Linux beim Ausführen eines Kommandos. Dazu erstmal ein paar Stichpunkte zu Prozessen:
- Jedes Programm was ausgeführt wird, wird zu einem Prozess.
- Unter Linux ist init die Mutter aller Prozesse.
- Neue Prozesse werden von bestehenden Prozessen erzeugt und werden Kindprozesse genannt.
- Kindprozesse sind nicht alleine lebensfähig. Wird also der Mutterprozess beendet, stirbt der Kindprozess.
Das sollte die Regel sein. Ausnahmen bilden so genannte Zombie-Prozesse.
Warum ist diese Kenntnis wichtig für den Umgang mit der Shell? Auch die Shell selbst ist ein Prozess und man muss wissen, wie die Verarbeitung von Kommandos in der Shell abläuft.
Der Regelfall ist die Vordergrundverarbeitung. Hier kommt das forking (Abgabelung) zum tragen. Das bedeutet, dass beim Ausführen die Shell dupliziert und mit dem auszuführenden Kommando überladen (übergeben) wird.

Nachdem das Kommando also abgesetzt wurde, wird von der Shell eine Subshell als Kindprozess initialisiert. Die Shell selber geht daraufhin in einen wartenden Zustand über, während die Subshell das Kommando mit dem vorangestellten Befehl execute ausführt. Dadurch wird verhindert, dass die Subshell selbst wieder eine Sub-Subshell erzeugt, was sich ins Unendliche verlaufen würde. Ist das Kommando abgearbeitet, sendet sie ein SIGCHLD-Signal. Die Shell wird wieder aktiviert und die Subshell quittiert.
Das Gegenstück zur Vordergrundverarbeitung ist die Hintergrundverarbeitung. Das Prinzip ist erstmal identisch mit dem der Vordergrundverarbeitung. Auch hier kommt das forking zum tragen und die Subshell wird ein Kindprozess der Shell.

Der entscheidende Unterschied ist, dass die Shell als Mutterprozess nicht in den sleep-Zustand übergeht, sondern weiterhin Eingaben des Benutzers annehmen und verarbeiten kann. Realisiert wird diese Verarbeitungsmethode durch das Anhängen des kaufmännischen Und ( & ) an das Kommando.
Da ein Kommando in der Regel recht schnell abgearbeitet wird, wird in folgendem Beispiel das Programm updatedb verwendt:
Dieses Programm sollte auf jedem aktuellen System standardmäßig installiert sein. Es aktuallisiert den Index einer Suchdatenbank für Suchbefehle wie locate, slocate oder mlocate und muss mit root-Rechten ausgeführt werden. Was in diesem Fall nur von Interesse sein soll, ist die Abarbeitungsdauer dieses Programms. Da es grundsätzlich einige Zeit in Anspruch nimmt, lassen sich die weiteren Erläuterungen besser darstellen. Die angestellte Option -v dient nur der ausgiebigen Bildschirmausgabe.
Als Resultat erhält man die Job-Nummer, sowie die Prozessnummer des Hintergrundprozesses und landet umgehend wieder am Eingabeprompt. Mit dem Befehl jobs lassen sich alle initiierten Hintergrundprozesse anzeigen:
Um wieder auf die Shell mit dem Hintergrundprozess zuzugreifen, verwendet man den Befehl fg, gefolgt von der Job-Nummer.
...in diesem Fall (mit updatedb) erhält man die Ausgabe der Abarbeitung des Suchindexes in der Subshell...
... /var/lib/vim/addons /var/lib/x11/X.roster /var/lib/x11/Xwrapper.config.md5sum /var/lib/x11/Xwrapper.config.roster ...
Ist der Hintergrundprozess abgearbeitet, wird wieder das SIGCHLD-Signal ausgeführt und man steht am Eingabeprompt der Shell.
Die Hintergrundverarbeitung ist vor allem dann nützlich, wenn man Programme startet, welche während der Verarbeitung keine Eingaben ermöglichen. Beispielweise Video-/Audioencoder. Natürlich könnte man auf einem lokalen System einfach eine neue Shell öffnen, aber bei entfernten Systemen ist dies nicht unbedingt vorteilhaft.
Weitere Informationen:
- Für die Ausführung von länger andauernden Programmen, welche die Eingabe blockieren, empfiehlt sich das Programm Screen. Dieses Programm kann als eigenständiger Prozess mehrere Shell's verwalten, die selbst und alle darin laufenden Prozesse, auch nach dem Ausloggen noch aktiv bleiben.
|