Shells für Linux und Unix richtig nutzen

Aus Zebradem WIKI
Zur Navigation springenZur Suche springen

Das Board mit Freiheiten




Hilfreich: In der Datei /etc/shells steht, welche Shells auf einem Linux-System unterstützt werden

Was für Windows die Eingabeauffordung, ist unter Linux / Unix die Shell. Der Benutzer hat dabei die Wahl zwischen verschiedenen Shells, von denen wir hier acht detaillierter vorstellen. Sie ermöglichen es dem Nutzer, direkten Kontakt zum Betriebssystem aufzunehmen. Linux- / Unix-Nutzer sind in der Wahl ihrer Werkzeuge wesentlich freier als Windows-Anwender. Sie können einerseits zwischen verschiedenen grafischen Desktops wie Enlightenment, GNOME, KDE, LXDE und Xfce wählen. Und während es auf der Konsole in Windows nur cmd.exe und die sogenannte Powershell gibt, kann man in Unix verschiedene Shells als Kommandointerpreter nutzen. Welche das sein können, steht in der Datei /etc/shells, die etwa in Ubuntu folgenden Inhalt hat:








# /etc/shells: valid login shells
/bin/csh
/bin/sh
/usr/bin/es
/usr/bin/ksh
/bin/ksh
/usr/bin/rc
/usr/bin/tcsh
/bin/tcsh
/usr/bin/esh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/screen

Von den gelisteten Shells sind nicht alle installiert, lediglich sh, dash, bash, rbash und screen stehen in der Anfangskonfiguration zur Verfügung. Die anderen kann der Administrator bei Bedarf nachträglich installieren. Möchte man eine Shell ausprobieren, kann der Benutzer diese direkt am Prompt mit der Eingabe des Befehls starten. Eine Übersicht über die Möglichkeiten liefern die entsprechenden Manpages.

Shell wechseln

Freie Auswahl: Mit dem Befehl chsh kann der Benutzer die Login-Shell wechseln. Die Manpage erklärt, wie das geht.

Wenn Sie zu einer anderen Shell wechseln wollen, hilft das Kommando chsh. Hier wird zuerst die aktuelle Shell angezeigt, anschließend kann eine neue eingegeben werden; achten Sie darauf, dass Sie den gesamten Pfad angeben, etwa /bin/dash. Die einzige Einschränkung für die Login-Shell ist, dass der Name des Befehls in der Datei /etc/shells enthalten sein muss. Root darf allerdings jeden beliebigen Befehl ausführen.

Bei Konten mit einer eingeschränkten Login-Shell kann diese nicht geändert werden. Die Manpage zu chsh empfiehlt, diese nicht in die Datei /etc/shells einzutragen, weil Benutzer nicht von sich aus zu der ursprünglichen Login-Shell zurückwechseln können. Diesem Rat folgend sollte ein Ubuntu-Administrator die Zeile /bin/rbash in der Datei /etc/shells löschen oder auskommentieren.


Tipp: Will der Administrator für alle künftigen Benutzer die Shell wechseln, tut er das am besten in der Datei /etc/adduser.conf. Dort steht in der Zeile "DSHELL=" die Standard-Shell für Neuzugänge.


Die Bourne-Shell: sh

Stephen Bourne - nicht zu verwechseln mit dem Filmagenten Jason Bourne - hat in den 1970er-Jahren die Bourne-Shell sh entwickelt. Sie war Ersatz für die Shell von Unix-Vater Ken Thompson, dessen Thompson-Shell die erste Unix-Shell war. Die Bourne-Shell gilt als Vorfahre der meisten heutigen Shells; sie besaß damals bereits alle wichtigen Funktionen wie Ein- und Ausgabeumlenkung, Pipes und Hintergrundprozesse. Unter Unix laufende Skripte sind meist für die Bourne-Shell geschrieben und funktionieren auch in den meisten anderen Shells. Damit es keine Probleme gibt, liegt deshalb auch immer eine Bourne-kompatible Shell hinter dem Kommando /bin/sh. Die Bourne-Shell beherrscht Programmierfunktionen wie "if - then - elif - fi", "case - in - esac" und "for - while - do - od". Kritik an der Bourne-Shell betraf und betrifft hauptsächlich die mangelnde Interaktivität, wie sie etwa die C-Shell mit Job-Kontrolle und History-Funktion bietet. Auch ist die Bourne-Shell teilweise verhasst, weil ihre Grammatik mehr der Programmiersprache ALGOL ähnelt denn C wie im Rest von Unix. Bekrittelt wird auch immer wieder, dass selbst einfache arithmetische Funktionen mithilfe von Tools wie test und expr erledigt werden müssen.


Die Bourne-again-Shell: bash

Gut zu wissen: Die Manpage von bash beschreibt detailliert, wie man mit der Shell arbeitet und wie Shell-Skripte programmiert werden.

Die bash ist Teil des GNU-Projekts. Sie wurde Ende der 1980er-Jahre von Brian Fox geschrieben. In weiten Teilen ist die bash kompatibel zur Bourne-Shell sh, hat allerdings einen größeren Funktionsumfang. Sie beherrscht Funktionen der Korn-Shell sowie der C-Shell, etwa die Kommando-History oder das Bearbeiten der Befehlszeile. Die bash ist die Standard-Shell auf einigen Linux-Systemen sowie unter Mac OS X. Einige Befehle sind in der bash einfacher verwirklicht als in der Original-Bourne-Shell. So kann bash beispielsweise die Standardausgabe und die Standardfehlermeldung mit dem &>-Operator gleichzeitig umlenken. In der Bourne-Shell muss so etwas mit einem Befehl wie "BEFEHL > file 2>&1" erledigt werden

Die Almquist-Shell

Die Almquist-Shell (ash) von Kenneth Almquist ist eine Neuimplementierung der SVR4-Variante der Bourne-Shell. Sie wurde ursprünglich für die traditionellen BSD-Varianten von Unix geschrieben, nachdem die Bourne-Shell selbst nur noch mit Lizenzierung zu erhalten war. Spätere Varianten orientieren sich am POSIX-Standard und eignen sich sehr gut zur Ausführung von Skripten. Die FreeBSD- und NetBSD-Projekte verwenden weiterentwickelte Versionen der ash als Standard-Shell (/bin/sh). Da die ash im Vergleich zu anderen modernen Shells sehr geringe Ansprüche an Speichergröße und Rechnergeschwindigkeit stellt, wird sie auch gerne in Embedded-Linux-Systemen verwendet; sie wurde auch in die ebenfalls im Embedded-Bereich verbreitete Multifunktions-Binary BusyBox integriert. Die Debian-Almquist-Shell

Die C-Shell: csh

Für BSD wurde die C-Shell entwickelt. In dieser kann bereits die Kommandozeile editiert werden; außerdem besitzt die C-Shell eine History-Funktion, über die frühere Kommandos wiederholt werden können. Darüber hinaus hat der Entwickler Bill Joy eine Job-Kontrollmöglichkeit implementiert. Mit der Tastenkombination [Strg-Z] kann das Ausführen eines Kommandos gestoppt und dann beispielsweise mit dem Befehl bg in den Hintergrund verschoben werden. Die C-Shell wird heutzutage kaum noch benutzt. Das mag unter anderem an einigen Unzulänglichkeiten der Skriptmöglichkeiten liegen: So werden beispielsweise Fehlermeldungen nur auf der Standardausgabe ausgegeben. Andere Shells wie die Tenex-C-Shell tcsh und die Korn-Shell sind inzwischen in die Fußstapfen der C-Shell getreten und haben diese weitgehend abgelöst.

Die Debian-Almquist-Shell: dash

Die dash (Debian-Almquist-Shell) ist in Debian und Ubuntu die Standard-Shell für Shell-Skripte. Weil die dash aber im interaktiven Modus unkomfortabel ist, bleibt die bash die User-Standard-Shell. Ziel der dash-Entwicklung war es, die Shell so klein wie möglich zu halten und die Ausführung der Skripte dementsprechend zu beschleunigen. Die POSIX-konforme Implementation von /bin/sh ist ein Abkömmling der NetBSD-Version der ash (Almquist Shell), und die ist wiederum eine Neuimplementierung der Bourne-Shell. Die dash wurde 1997 von BSD auf Linux portiert und heißt seit 2002 dash. In der Manpage empfehlen die Entwickler, Setuid-Shell-Skripte unbedingt zu vermeiden, da diese ein erhebliches Sicherheitsrisiko darstellen. Ein kleineres Problem, auf das die Entwickler hinweisen: Die Parameter der drei möglichen Promptstrings sollten festgelegt werden, bevor sie angezeigt werden.

Die Restricted Shell: rbash

Einschränkung umgehen: Mit dem Befehl ":set shell=/bin/sh" im Editor vi kann der Benutzer einer einschränkende Shell diese aushebeln.

Die rbash ist eine eingeschränkte bash-Shell (unter Restricted Shell). Die Shell kann auf zwei Arten gestartet werden: indem der Benutzer sie mit dem Befehl rbash aufruft oder mit "bash -r". Die Restricted Shell dient dazu, eine kontrollierte Umgebung zu bieten. So wird sie hin und wieder in chroot-Gefängnissen genutzt, um den Systemzugriff noch weiter einzuschränken. Unter anderem ist in der rbash zum Beispiel der Verzeichniswechsel mit cd nicht möglich. Auch können keine Umgebungsvariablen wie SHELL und PATH geändert werden. Ebenso ist das Umleiten der Ausgabe mit >, >> etc. abgeschaltet. Restricted-Shell-Modi gibt es außer in der bash auch in der Bourne- und in der Korn-Shell. Die rbash ist allerdings nicht vollständig abgesichert. Ein Benutzer kann beispielsweise einfach aus der eingeschränkten Umgebung ausbrechen, wenn er ein Programm benutzt, das seinerseits Shell-Zugriff ermöglicht. Im Standard-Unix-Editor vi sind dazu beispielsweise nur zwei Befehle erforderlich:



:set shell=/bin/sh
:shell

Screen-Shell: screen

Verräterisch: Welche Tastenkombinationen der Fenstermanager screen bietet, zeigt er nach der Eingabe von [Strg-A-?].

Screen passt eigentlich nicht ganz in die typischen Shells, denn das Programm geht teilweise darüber hinaus. Screen ist ein Fenstermanager für die Konsole. Damit kann der Benutzer über ein einziges Terminal mehrere virtuelle Sitzungen verwalten. In diesen können verschiedene Programme laufen, und der Benutzer kann zwischen diesen mit der Tastenkombination [Strg-Alt-Leertaste] hin- und herschalten. Befehle und Programme kann man mit [Strg-A-D] von der aktuellen Sitzung trennen; das jeweilige Programm läuft dann im Hintergrund weiter. Wenn sich der Benutzer anschließend aus- und wieder einloggt, kann die vorherige Sitzung mit "screen -r" wiederhergestellt werden.

Die Tenex-C-Shell: tcsh

Tipp: Unter dieser Webpage werden die Eigenschaften verschiedener Shells verglichen.

Die Tenex-C-Shell tcsh ist vollständig kompatibel zur C-Shell. Allerdings hat der Entwickler Christos Zoulas die Editiermöglichkeiten auf der Kommandozeile verbessert. Außerdem bietet tcsh eine programmierbare Dateinamenvervollständigung, und Dateien können direkt auf der Kommandozeile bearbeitet werden. Tenex war ein in den 1960er-Jahren entwickeltes, alternatives Betriebssystem für die DEC PDP-10. Es glänzte vor allem aufgrund eines vollständigen virtuellen Speichersystems und wegen des benutzerorientierten Kommandointerpreters. Im Gegensatz zu anderen Systemen der damaligen Zeit nutzte Tenex lange Befehlsnamen und auch bereits sogenannte "noise words", um noch mehr Klarheit in den Befehlen zu schaffen. Damals wurde anstelle von ls (list directory content) der Befehl Directory of Files genutzt (of Files konnte als noise words weggelassen werden). Es genügte aber, DIR zu tippen und die ESC-Taste zu drücken, um die Befehle zu ergänzen. Das funktionierte sogar schon mit Dateinamen. Tenex hatte darüber hinaus bereits ein eingebautes Hilfesystem, das mit dem Fragezeichen aufgerufen wurde.

Skripte schreiben und starten

Skripte werden zumeist in der Bourne-Shell-Syntax geschrieben. Das ist der kleinste gemeinsame Nenner unter den Shells und damit ist die Kompatibilität zu den meisten Shells garantiert. Um zu gewährleisten, dass auch die Bourne-Shell oder eine kompatible als Skriptinterpreter genutzt wird, schreiben Sie in die erste Zeile des Skripts

#!/bin/sh

Um ein Skript anschließend zu starten, müssen Sie es zunächst ausführbar machen. Das geht mit dem Befehl

chmod u+x Shellskript

Gestartet wird das Skript dann aus dem Verzeichnis mit

./Shellskript

Wichtig dabei: Vergessen Sie nicht den Punkt und den Schrägstrich für den aktuellen Pfad. Bedingungen in Skripten

In Skripten kann man Bedingungen und Variablen verarbeiten und Schleifen bilden. Bedingungen werden mit if gebildet, etwa:

#!/bin/sh
if [ -f datei.ext ]
then
cp datei.ext ~/test1/
else
echo "Datei nicht vorhanden"
fi

Hier wird zunächst mit if geprüft, ob es die zu kopierende Datei überhaupt gibt. In den eckigen Klammern steht die Bedingung, die überprüft werden soll; falls ja, wird kopiert, falls nicht, erscheint eine Fehlermeldung. Welche Bedingungen tes" sonst noch abfragen kann, erfahren Sie mit man test. if-Falle I: Vergessen Sie nicht, dass jedes "if" in Bash-Skripts auch ein "then" braucht. Und am Schluss des if-Konstrukts muss ein fi stehen. if-Falle II: Wenn Sie Testbedingungen in eckige Klammern schreiben, so muss zwischen Bedingung und Klammer immer ein Leerzeichen stehen, also

if [ $# -gt 0 ]

und nicht

if [$# -gt 0]

Schleifen programmieren

Schleifen kann man in Shell-Skripten ebenfalls programmieren. Dazu dient der for-do-Befehl

#!/bin/sh
FILELIST="datei1.ext datei2.ext datei3.ext"
for FILENAME in $FILELIST
do
if test -f $FILENAME; then
cp $FILENAME ~/test1/
cp $FILENAME ~/test2/
else
echo "Datei $FILENAME nicht vorhanden"
fi
done

Hier stehen ein paar Dateinamen in der Variablen FILELIST. Der Befehl for erkennt die Liste anhand der enthaltenen Leerzeichen und geht sie der Reihe nach durch. Jeder Listeneintrag wird der Variablen FILENAME zugewiesen. Danach wird die Datei kopiert, die gerade an der Reihe ist. Beachten Sie, dass nach einem for ein do stehen muss und dass ans Ende der Schleife ein done gehört.

Kommandozeilenparameter im Skript

Noch ist das Skript nicht sehr eigenständig. Besser wäre es, wenn Sie die obige Liste als Kommandozeilenparameter übergeben könnten, also in der Form: ./Shellskript datei1.ext datei2.ext und so weiter. Das geht so:

#!/bin/sh
if [ $# -lt 1 ]; then
echo "Fehler: Kein Dateiname eingegeben"
echo "Benutzung: $0 datei.ext [datei2.ext] [datei.ext]"
fi
while [ $# -gt 0 ]
do
FILENAME=$1
if test -f $FILENAME; then
echo "Kopiere $FILENAME"
cp $FILENAME ~/test1/
cp $FILENAME ~/test2/
else
echo "Datei $FILENAME nicht vorhanden"
fi
shift
done

Damit versteht das Programm auch Kommandozeilenparameter. Zunächst allerdings prüft es, ob die Parameter vorhanden sind. Das geht am besten mit der Spezialvariablen $#. Die enthält die Anzahl der übergebenen Parameter. Mit if [ $# -lt 1 ]; then prüft das Skript, ob weniger als ein Parameter vorhanden sind. Ist das der Fall, gibt das Skript eine Fehlermeldung und eine kurze Anleitung über die Benutzung aus. Dabei kommt $0 zum Einsatz. Diese Variable enthält den Programmnamen selbst. Sind genügend Parameter vorhanden, tritt eine While-Schleife in Kraft. Sie zählt bei jedem Durchlauf die Anzahl der vorhandenen Parameter mit while [ $# -gt 0 ]. Das -gt steht für "greater than". Solange diese Bedingung zutrifft, wird in der folgenden Schleife immer der Parameter $1 der Variablen FILENAME zugewiesen. Kurz vor dem Ende der Schleife kommt noch der Befehl shift ins Spiel. Der sorgt dafür, dass der erste Eintrag aus der Parameterliste gelöscht wird und dafür der nächste an seine Stelle trifft. So rücken also alle Parameter auf die Stelle von $1 und können abgearbeitet werden.

Fallen für Bash-Programmierer

Bash-Programmierung ist nur etwas für starke Gemüter - zumindest so lange, bis man sich an die Eigenheiten der Skript-Sprache gewöhnt hat. Bis dahin lauern jede Menge Fallen auf den Programmierer, unter anderem mit so banalen Dingen wie den Variablen. Variablen-Falle I: Bei der Zuweisung hat eine Variable kein vorangestelltes Dollarzeichen, also WERT="Hallo" Wird die Variable jedoch an anderer Stelle im Programm verwendet, so muss das Dollarzeichen vorangestellt werden, also echo $WERT Variablen-Falle II: Bei der Zuweisung einer Variablen darf vor und nach dem Gleichheitszeichen keinesfalls ein Leerzeichen stehen. Wer aus alter Programmierergewohnheit WERT = "Hallo" schreibt, hat schon verloren und produziert einen Fehler. Denn es muss so aussehen: WERT="Hallo" Fehlerfreie Bash-Programmierung bedarf größter Sorgfalt, allerdings bietet sie dafür viele Möglichkeiten.

Kompatibilität und Portabilität

Je nach Abstammung der Shell sind für sie geschriebene Skripte nicht oder nur bedingt kompatibel zu anderen Shells. Zum Beispiel lassen sich Bourne-Shell-Skripte nicht unter der C-Shell ausführen, aber es besteht eine gewisse Chance, dass ein solches Skript unter einer Korn-Shell oder Job-Control-Shell läuft. Neben diesem offensichtlichen Problem besteht aber auch das Problem, dass unterschiedliche Implementierungen der gleichen Shell existieren und dass Shells unter Beibehaltung ihres Namens im Laufe der Zeit auch weiterentwickelt wurden und werden. Die gleichen (gleich benannten) Shells auf unterschiedlichen Unix-Varianten weisen unterschiedliche Entwicklungsstände und somit sowohl unterschiedliche Eigenschaften als auch Fehler auf. Hinzu kommt, dass Shell-Skripte, wie bereits beschrieben, viele normale Unix-Kommandos aufrufen. Auch diese unterscheiden sich in ihrem Verhalten zum Teil von Unix zu Unix. Dadurch kann sich das Verhalten eines Shell-Skripts ebenfalls ändern, wenn es auf einem anderen Unix ausgeführt wird.

Eine gängige Strategie zum Schreiben von portablen Shell-Skripten ist es, sich nur auf den kleinsten gemeinsamen Nenner zu verlassen. Dies bedeutet in der Regel, dass man die Bourne-Shell verwendet und damit auf die bequemen Erweiterungen, wie sie zum Beispiel in der Korn-Shell oder bash zu finden sind, bewusst verzichtet. Je nachdem, wie groß die Bandbreite der abzudeckenden Systeme ist, verzichtet man auch auf die Benutzung neuerer, in POSIX standardisierter Bourne-Shell-Features.