Shells

Ein Unix-System ist modular aufgebaut wie nebenstehende Abbildung verdeutlicht. Die Hardware wird von entsprechenden Treibern unterstützt, auf die der Kernel aufsetzt. Die unterste Schnittstelle zum Benutzer ist ein sogenannter Kommandointerpreter oder auch Shell genannt. Sie führt Befehle aus, die vom Terminal oder aus Dateien gelesen werden.

Abbildung 1: Modularer Aufbau eines Unix-Systems

Die nächste Ebene besteht aus den Anwendungen wie z.B. Emacs, Midnight Commander, mutt oder gar squake. Wird das X-Window System benutzt, dann schiebt sich natürlich das X11-System als weitere Ebene dazwischen.

Immer wiederkehrende Befehle können in Dateien gesammelt werden, in sogenannten Shell-Skripten. So kann sich der Anwender leicht eigene Programme erzeugen. Auf diese Weise können relativ einfach Aufgaben erledigt werden, die auf den ersten Blick vielleicht schwierig erscheinen.

Die Aufgaben der Shell erstrecken sich von der Eingabe von Befehlen über die Deklaration von Variablen, Aliasen und Funktionen bis hin zu Prozeßkontrolle und Skript-Programmierung.

Für jeden Geschmack ist etwas dabei

Für Unix-Systeme existieren eine Vielzahl von Shells, die unterschiedlichen Konzepten folgen. Der Standard auf jedem Unix-System ist eine Bourne Shell. Sie wurde von der Free Software Foundation erweitert zu einer freien Variante, genannt Bourne Again Shell oder kurz bash. Dieses ist die Default-Shell auf den meisten Linux-Systemen, da sie im Gegensatz zur normalen sh frei vertrieben werden kann. Auf vielen Bootdisketten hingegen findet man eine recht spartanische Variante, nämlich die ash, die sehr klein ist und dennoch Basisfunktionen einer Bourne Shell bietet. In den Grundfunktionen kompatibel ist ebenfalls die Korn Shell bzw. ihr freier Vertreter, die pdksh.

Besonders interessant für C-Programmierer ist die an der Universität zu Berkeley in Kalifornien entwickelte C-Shell. Dank ihrer an C angelehnten Syntax und speziellen Ausrichtung ist sie für komplexe Shell-Skripte teilweise besser geeignet als eine bash.

Daneben gibt es noch weitere Ansätze, z.B. die Z-Shell, welche aus der Korn Shell entstanden ist. Power-User können sich zudem noch an einer rc, kiss oder flin erfreuen, auf die hier nicht weiter eingegangen wird. Wir werden uns auf die Default-Shell, nämlich die bash, konzentrieren.

Tastaturbelegung

Die für den Anwender wichtigste Eigenschaft ist die Möglichkeit, die Kommandozeile zu bearbeiten. Dazu bietet die bash über die readline-Bibliothek zwei Modi an: einen Emacs-Style und einen VI-Style. Ergänzend können die Tasten auch einzeln umdefiniert werden.

Eine ausführliche Beschreibung der Funktionen und voreingestellten Verknüpfung mit Tastencodes befindet sich in der Manual-Seite der bash. An dieser Stelle werden lediglich die wichtigsten Verknüpfngen des voreingestellten Emacs-Styles vorgestellt. Die meisten dieser Tastenkombinationen sind mit Funktionen belegt, die im Emacs die gleichen Auswirkungen haben.

Im folgenden wird daher auch gleich die Emacs-Schreibweise verwendet. C-x bedeutet Control-x und M-x steht für Meta-x, Meta entspricht meistens der linken Alt-Taste, kann jedoch auch durch die ESC-Taste dargestellt werden.

Die Cursortasten können durch C-b (backward) und C-f (forward) ersetzt werden, was besonders wichtig ist, wenn die Cursortasten nicht mehr zur Verfügung stehen, z.B. wenn man sich von einem anderen Rechner eingelogt hat und die Terminalemulation nicht ganz paßt. Mit M-b und M-f wird der Cursor jeweils um ein Wort nach links bzw. rechts bewegt. Mit C-a gelangt man schließlich an den Zeilenanfang und mit C-e an das Zeilenende.

Mit C-u wird der Text bis zum Zeilenanfang und mit C-k bis zum Zeilenende gelöscht. Dabei wird der gelöschte Text in den Ring-Puffer kopiert und er kann jederzeit mit C-y wieder eingefügt werden. C-d löscht wie üblich das Zeichen unterhalb des Cursors. Mit C-w kann das nächste Wort links und mit M-d rechts vom Cursor entfernt werden.

Ein großer Vorteil der bash ist ihre Gedächtnisfunktion (engl. history). Mit den Voreinstellungen speichert die bash die 500 letzten Befehle. In diesem Puffer kann mit Cursor-up und Cursor-down bzw. C-p und C-n gestöbert werden. Mit C-r und C-s kann in ihm rückwärts und vorwärts gesucht werden, gerade bei langen Befehlszeilen kann man sich so viel Tipparbeit sparen.

Für Schnelltipper interessant ist noch die Kombination C-t, die nämlich die Zeichen links vom Cursor und unter diesem vertauscht, M-t entspricht der gleichen Funktion für Wörter. Die Tasten C-l entsprechen dem Befehl Clear-Screen.

Mit der Tabulatortaste läßt sich die Arbeit stark erleichtern. Je nachdem, welche Zeichen unter dem Cursor stehen, werden Dateinamen, Programme oder gar Rechnernamen wie von Geisterhand ergänzt. Am Anfang der Zeile erweitert ein Druck auf die Tabulatortaste den angefangenen Befehl, nach einem Leerzeichen hingegen wird das angefangene Wort als Anfang eines Dateinamens im aktuellen Verzeichnis angesehen. Fängt das Wort hingegen mit einem Klammeraffen (@) an, dann sucht die bash sogar nach passenden Rechnernamen, allerdings nur aus der Datei /etc/hosts.

Deutsche Anpassungen

Es kann passieren, daß die bash Umlaute nicht richtig behandelt. Sollte dieses der Fall sein - ein Indiz dafür ist, daß sie piept, wenn man versucht, ein ä zu tippen - dann muß die Datei .inputrc im Benutzerverzeichnis (Achtung, den Punkt nicht vergessen) um folgende Zeilen ergänzt werden.

   set meta-flag on
   set convert-meta off
   set output-meta on
  

Eigene Anpassungen

Wie an den deutschen Anpassungen zu sehen war, läßt sich das Verhalten der bash in der Datei .inputrc manipulieren. Diese Datei wird von der readline-Bibliothek ausgewertet. Sie wird von verschiedenen Programmen verwendet, die selbst eine Eingabezeile anbieten.

Abbildung 2: Verarbeitung von Tastaturcodes

Standardeinstellung ist, daß alle gedrückten Tasten umgesetzt in Zeichen eingefügt werden. Das führt jedoch zu Problemen, wenn es sich nicht um darstellbare Zeichen, sondern um Tastaturcodes, handelt (drücken Sie einmal die Insert- oder eine Funktions-Taste). In diesem Fall kann die readline-Bibliothek über diese Konfigurationsdatei angewiesen werden, entsprechend zu handeln.

Oft funktionieren z.B. die Tasten Del, Home und End (bzw. Entf, Pos1 und End) nicht richtig obwohl die bash entsprechende Funktionen vordefiniert hat. Stattdessen erzeugen sie Piepser und fügen eine Tilde ein - nicht gerade das gewünschte Verhalten.

Mit den folgenden Zeilen werden den Tasten die richtigen Funktionen zugeordnet:

   "\e[1~": beginning-of-line
   "\e[3~": delete-char
   "\e[4~": end-of-line
  
Dieses Tastaturfeld enthält noch drei weitere Tasten, welche die gleichen Symptome zeigen, jedoch diesmal nicht auf eine passende Funktion abgebildet werden können. Das störende Gepiepse kann jedoch abgestellt werden, indem man die Tasten mit einer leeren Zeichenkette belegt und so stumm stellt. Dieses übernehmen die folgenden Zeilen.

   "\e[2~": ""
   "\e[5~": ""
   "\e[6~": ""
  
Auf diese Weise läßt sich die gesamte Tastatur samt Control- und Alt-Tasten umdefinieren.

Aliase und Variablen

Was einem Benutzer das Leben wirklich erleichtert sind Abkürzung von langen Befehlen und gespeicherte Optionen. Beides läßt sich einfach mit der bash realisieren.

Vielen ist der Befehl "ll" viel zu lang (50% immerhin) und sie hätten ihn gerne abgekürzt. Folgende Zeile erledigt das:

   alias l=ll
Da der Befehl ll oftmals nicht definiert ist, muß seine Definition folgen. Wird der Wert des Aliases in einfache Hochkommata gesetzt, so können auch Parameter an den Befehl weitergeben werden:

   alias ll='ls -lF'
Verschiedene Variablen steuern das Verhalten von Programmen, so bestimmt z.B. die Variable EDITOR, welcher Editor von Anwendungsprogrammen aufgerufen wird. Diese Variablen werden genauso gesetzt wie Aliase, jedoch fällt das Schlüsselwort weg. Folgender Befehl ergänzt den Suchpfad für Programme um ein Verzeichnis unterhalb des Benutzerverzeichnisses:

   PATH=$PATH:~/bin

Skript-Programmierung

Wie eingangs geschrieben, lassen sich mit einer Shell auch Programme schreiben, um wiederkehrende oder kompliziert aussehende Aufgaben schnell erledigen zu können. Diese sogenannten Shellskripte sind natürlich längst nicht so schnell wie entsprechende kompilierte C-Programme, jedoch lassen sie sich in erheblich geringerer Zeit schreiben und viel leichter ändern.

Jedes Skript ist eine Textdatei, bestehend aus Anweisungen. In der ersten Zeile enthält sie den Pseudo-Kommentar #!, der den zu verwendenden Interpreter beschreibt. Oft ist das /bin/sh oder /bin/csh, es können genausogut aber auch komplexere Skript-Sprachen wie z.B. /usr/bin/perl, /usr/bin/python oder /usr/bin/rexx verwendet werden.

In der Skriptsprache stehen alle wichtigsten Strukturen einer Programmiersprache zur Verfügung. Dieses beinhaltet Schleifen (for und while) und Verzweigungen (if und case) sowie Variablen und Funktionen. Nicht zu vergessen ist natürlich die Möglichkeit externe Programme zur Verarbeitung einzuschalten.

Für Umsteiger von DOS oder Windows ergibt sich z.B. oft das Problem, daß sie Dateien haben, die aus lauter Großbuchstaben bestehen. Linux bzw. Unix unterscheidet jedoch zwischen Groß- und Kleinbuchstaben, sodaß dieses zu Komplikationen führen kann.

Folgendes Skript benennt alle Dateien im aktuellen Verzeichnis in ihr Äquivalent, bestehend aus Kleinbuchstaben, um. Mit den sogenannten Backquotes (umgekehrte Anführungszeichen) wird die Shell angewiesen, die einschlosse Zeichenkette als Befehl zu interpretieren und deren Ergebnis auszugeben. Hier wird es in der Variablen newname gespeichert.

   #! /bin/sh

   for file in *
   do
     newname=`echo $file|tr [:upper:] [:lower:]`
     mv $file $newname
   done
  
Formuliert man diesen Befehl als Shellfunktion, der in der Datei ~/.bashrc definiert wird, dann ist er sogar noch schneller. Die folgende Funktion kann wie ein Programm aufgerufen werden. Es werden im Gegensatz zu obigem Programm jedoch nur die als Parameter angegebenen Dateien umbenannt.

   rename_files()
   {
     for file in $*
     do
       newname=`echo $file|tr [:upper:] [:lower:]`
       mv $file $newname
     done
   }
  

Prozeßkontrolle

Für jedes aufgerufene Programm erzeugt die Shell einen Tochterprozeß und wartet auf dessen Beendigung. Diese Programme laufen im Vordergrund und haben die Kontrolle über die Ein- und Ausgabegeräte. Bei vielen Programmen ist es jedoch garnicht gewünscht, daß sie im Vordergrund laufen.

Die meisten Programme, die unter X11 arbeiten, würden so jedesmal eine Shell belegen. Sie selbst benutzen jedoch keinerlei Ein- oder Ausgaben auf dem Terminal, sondern arbeiten ausschließlich mit dem X11-System zusammen.

Solche Programme werden mit einem angehängten kaufmännischen Und (&) aufgerufen. Dieses Zeichen weist die verwendete Shell an, nicht auf die Beendigung des Tochterprozesses zu warten, sondern einen parallel arbeitenden Prozeß zu erzeugen, er wird quasi "in den Hintergrund" geschickt.

Zur Prozeßkontrolle gehört ebenfalls, daß Prozesse nachträglich auf Hintergrundarbeit umgestellt werden können sowie die Möglichkeit, den aktuellen zeitweise zu unterbrechen. Die meisten Programme lassen sich mit der Tastenkombination C-z zeitweise anhalten, während der Anwender wieder auf der ursprünglichen Shell landet. Dort kann der Prozeß mit dem Befehl "fg" wieder zum Leben im Vordergrund erweckt und mit "bg" in den Hintergrund geschickt werden. Beide internen Befehle können als Argument eine Jobnummer angehängt bekommen, sodaß sie nicht den letzten Prozeß behandeln.

Wie in obigem Beispiel zur Skriptprogrammierung zu sehen ist, können verschiedene Befehle hintereinandergeschaltet werden. Das zweite Programm bekommt dabei die Ausgabe des ersten als Eingabe und kann sie weiterverarbeiten. Dazu werden die Programme mit dem Pipe-Symbol "|" verknüpft.

Literatur

Stephen R. Bourne; Das Unix System V; Addison-Wesley ISBN 3-925118-23-2

Sebastian Hetze et al; Linux Anwenderhandbuch, LunetiX Softfair ISBN 3-929764-05-9

Martin Schulze

Quelle: Linux-Special vom Toolbox-Verlag '97


© Joey, 26 Dec '97