Vorlesung "UNIX"

von Prof. Jürgen Plate

2 Einführung in die Shell

Die Shell ist der Kommandointerpreter von UNIX. Es haben sich drei Typen entwickelt:

Aus diesen sind weitere Shells entwickelt worden, z. B. die Bourne again shell, bash, die ähnliche Funktionen hat wie die ksh oder die tcsh als Abkömmling der csh.

2.1 Aufgaben der Shell

Es gibt weiterhin die Möglichkeit, Programme "im Hintergrund" zu starten und am Bildschirm weiterzuarbeiten, während das Programm läuft. Sobald die Shell bereit ist, Kommandoeingaben anzunehmen, meldet sie sich mit einem Bereitschaftszeichen (Prompt). Im einfachsten Fall ist dies ein "$"-Zeichen als normales Prompt, dem "#" als Prompt für Superuser und dem ">"-Prompt, wenn noch weitere Eingaben erwartet werden. Wie vieles in der Shell, kann der Prompt beliebig modifiziert werden.

Anmerkungen:

  1. In diesem Kapitel geht es um grundlegende Eingenschaften der Shell, später wird das Thema noch vertieft.
  2. Die Shell ist ein ganz normales Programm, das von der Standardeingabe (Tastatur) liest und auf die Standardausgabe (Bildschirm) ausgibt. Wenn das Dateiende erreicht wird (End Of File, EOF), z. B. durch Eingabe von CTRL-D, terminiert sie. Ist es die Login-Shell, erfolgt ein Logoff des Benutzers.
  3. Die Shell kann wie ein Programm als Subshell aufgerufen werden (Schachtelung). Dies wird beispielsweise benötigt, wenn man Shell-Programme (shell skripts) testen will.
  4. Suchpfade für Kommandos: Es gibt zwei Möglichkeiten, den Pfad für ein Kommando festzulegen:
    1. Direkt als absoluter oder relativer Pfadname
    2. Indirekt über den Pfad, der in der Shell-Variablen PATH gespeichert ist.

Kommandobeschreibung

In der Literatur (auch beim Kommando "man") werden die Kommandos in Kurzform beschrieben. Dabei werden Werte, die optional sind, in eckige Klammern gesetzt. Die (meist einbuchstabigen) Optionen werden als Kette aufgelistet, z.B. [-aeGhlz]. Das bedeutet nichts anderes, als daß eine beliebige Kombination dieser Optionen möglich ist (ob sie sinnvoll ist, wird hier nicht berücksichtigt).

UNIX-Benutzer sind "mündig"! Was heißt das? Wenn Sie ein Kommando eingeben, das die gesamte Platte löscht, fragt das Programm nicht noch einmal nach, ob Sie das auch wirklich wollen, sondern löscht die Platte sofort.

Beim Testen von Shell-Programmen hilft das Kommando

echo [Argumente]

Dieses Kommando gibt die Argumente auf dem Bildschirm aus. Für den Einsteiger ist das Kommando wichtig, weil er so die Kommandobearbeitung der Shell recht gut verfolgen und studieren kann (Einstreuen von echo-Kommandos in die Befehlsfolge).

Bearbeitung der Kommandozeile durch die Shell

Ein Kommando wird erst ausgeführt, wenn der Benutzer am Ende der Kommandozeile die RETURN-Taste drückt. Eine genauere Kenntniss des dann vonstatten gehenden Ablaufs erlaubt es, zu verstehen, warum etwas nicht so klappt, wie man es sich vorgestellt hat. Daher sollen diese Schritte hier kurz beschrieben werden. (Anmerkung: Einige der Kommandotrenner, `...`, Jokerzeichen und Variablen werden erst später behandelt.):
  1. Die Shell liest bis zum ersten Kommandotrenner (& && || ; > <) und stellt fest, ob Variablenzuweisungen erfolgen sollen oder die Ein-Ausgabe umgelenkt werden muß.
  2. Die Shell zelegt die Kommandozeile in einzelne Argumente. Sie trennt die einzelnen Argumente durch eines der Zeichen, die in der Shell-Variablen IFS (Internal Field Separator) stehen, normalerweise Leerzeichen, Tabs und Newline-Zeichen.
  3. Variablenreferenzen, die in der Kommandozeile stehen, werden durch ihre Werte ersetzt.
  4. Kommandos, die in `...` oder bei der bash in $(...) stehen, werden ausgeführt und durch ihre Ausgabe ersetzt.
  5. stdin, stdout und stderr werden auf ihre "Zieldateien" umgelenkt.
  6. Falls in der Kommandozeile noch Zuweisungen an Variablen stehen, werden diese ausgeführt.
  7. Die Shell sucht nach Jokerzeichen und und ersetzt diese durch passende Dateinamen.
  8. Die Shell führt das Kommando aus.

2.2 Ein-und Ausgabeumleitung

Die drei dem Terminal zugeordeneten Dateikanäle stdin, stdout und stderr können jederzeit auf Dateien umgeleitet werden. Den drei Standarddateien sind die Filehandles 0 (stdin), 1 (stdout) und 2 (stderr) zugeordnet.

Eingabeumleitung

Das Programm liest nun nicht mehr von der Tastatur (stdin), sondern aus einer Datei, bis das Dateiende erreicht ist.

Die Eingabeumleitung erfolgt durch das Zeichen "<", gefolgt von einem Dateinamen.

Kommando < Dateiname

Statt z. B. beim write-Kommando den Text direkt einzugeben, kann auch einen Datei an ein anderes Terminal gesendet werden:

write markus < Nachricht

Ausgabeumleitung

Die Ausgabe des Programms wird nicht auf dem Bildschirm (stdout) ausgegeben, sondern in einen Datei geschrieben. Die Ausgabeumleitung erfolgt durch das Zeichen ">", gefolgt von einem Dateinamen.

Falls die Datei noch nicht vorhaden war, wird sie automatisch angelegt. Falls die Datei schon vorhanden ist, wird sie überschrieben, d. h. es wird immer ab dem Dateianfang geschrieben.

Kommando > Dateiname

Fehlermeldungen (stderr) erscheinen nach wie vor auf dem Bildschirm. Beispiel: Ausgabe der Verzeichnisbelegung in einen Datei:

ls -l > info

Umlenkung der Fehlerausgabe (stderr)

Die Umleitung der Fehlerausgabe erfolgt genauso, wie die Ausgabeumleitung, jedoch wird hier die Zeichenfolge "2>" verwendet, da stderr die Handlenummer 2 hat.

Kommando 2> Fehlerdatei

(Die Umleitung der Standardausgabe ist nur die Kurzform von Kommando 1> Dateiname). Natürlich ist eine beliebige Kombination von Ein- und Ausgabeumleitung möglich, z. B.

Kommando < Eingabedatei > Ausgabedatei 2> Fehlerdatei

Anhängen von Infos an eine Datei

Es ist auch möglich, die Ausgabe des Programms an eine bereits vorhandene Datei anzuhängen. Dazu wird des ">" doppelt geschrieben.

Kommando >> Sammeldatei

Dazu ein paar Beispiele:

Dateiliste und aktive Benutzer in einen Datei schreiben:

ls -l > liste
who >> liste

Dur Umleitung von Ein- und Ausgabe läßt sich auch unterdrücken. Für die Ausgabe schreibt man

Kommando > /dev/null

oder für die Fehlerausgabe

Kommando 2> /dev/null.

Beides läßt sich auch kombinieren:

Kommando > Ergebnisdatei 2> /dev/null.

Will man ein Programm mit einem beliebigen Eingabedatenstrom versorgen, schreibt man

Kommando < /dev/zero.

Die Umleitung von stout und stderr in dieselbe Datei würde prinzipiell eine zweimalige Angabe der Datei (eventuell mit einem langen Pfad) erfordern. Für die Standarddateien werden in solchen Fällen spezielle Platzhalter verwendet:

&0Standardeingabe
&1Standardausgabe
&2Standard-Fehlerausgabe

Kommando > ausgabe 2>&1

(Es ist sogar möglich, in Shellskripts anonyme Dateien zu eröffnen, die nur über die Dateinummer (Handle) referenziert werden).

2.3 Pipes

Eine Pipe verbindet zwei Kommandos über einen temporären Puffer, d. h. die Ausgabe vom ersten Programm wird als Eingabe vom zweiten Programm verwendet. Alles, was das erste Programm in den Puffer schreibt, wird in der gleichen Reihenfolge vom zweiten Programm gelesen. Pufferung und Synchronisation werden vom Betriebssystem vorgenommen. Der Ablauf beider Prozesse kann verschränkt erfolgen. In einer Kommandofolge können mehrere Pipes vorkommen. Der Pipe-Mechanismus wird durch das Zeichen "|" (senkrechter Strich) aktiviert:

Kommando 1 | Kommando 2

Beispiel: Ausgabe der Dateien eines Verzeichnisses mit der Möglichkeit, zu blättern:

Natürlich können auch mehrere Kommandos hintereinander durch Pipes verbunden werden:

Kommando 1 | Kommando 2 | Kommando 3 | Kommando 4 | ...

Solche Kommandofolgen werden auch als Filter bezeichnet. Einige nützliche Filter sind in jedem UNIX-System verfügbar. Zum Beispiel:

head [-n] [datei(en)]
Ausgabe der ersten n Zeilen aus den angegebenen Dateien. Voreinstellung ist 10 Zeilen. Wird keine Datei angegeben, liest head von der Standardeingabe.

tail [-/+n] [bc[f|r]] [datei]
Ausgabe der letzten n Zeilen einer Datei. Voreinstellung für n ist 10. Wird keine Datei angegeben, liest tail von der Standardeingabe.

Optionen
+nab der n. Zeile ausgeben
-ndie letzten n Zeilen ausgeben Wird hinter die Zahl n ein 'b' gesetzt (z. B. -15b), werden nicht n Zeilen, sondern n Blöcke ausgegeben. Wird hinter die Zahl n ein 'c' gesetzt (z. B. -200c), werden nicht n Zeilen, sondern n Zeichen (characters) ausgegeben.
-r Zeilen in umgekehrter Reihenfolge ausgeben (letzte zuerst). Geht nicht bei GNU-tail - stattdessen kann man das Programm toc verwenden.
-f tail am Dateiende nicht beenden, sondern auf weitere Zeilen warten. (Ende des Kommandos mit der CTRL-C-Taste). Damit kann man z. B. Logfiles beobachten, die ständig wachsen.

tee [-i] [-a] [datei]
Pipe mit T-Stück: Kopiert von stdin nach stdout und schreibt die Daten gleichzeitig in die angegebene Datei.

Optionen
-iIgnorieren von Interrupts (Unterbrechungs-Taste)
-aAnhängen der Info an die angegebene Datei (Voreinstellung: Überschreiben der Datei)

wc [-lwc] [Datei(en)]
Dieses Kommando zählt Zeilen, Worte oder Zeichen in einer Datei. Wird kein Dateiname angegeben, liest wc von der Standardeingabe. Normalerweise zählt man damit in Skripten irgendwelche Ergebnisse. Optionen:

-l Zähle Zeilen
-wZähle Worte
-cZähle Zeichen

Weitere Filter sind more, less, tr, ....

2.4 Metazeichen zur Expansion von Dateinamen

Damit man beim Angeben von z. B. Dateinamen nicht alle Namen eintippen muß, sondern die Dateien auch alle oder nach bestimmten Kriterien auswählen kann, gibt es Metazeichen (Jokerzeichen, Wildcards). Im Gegensatz zu anderen Systemen (z. B. MS-DOS) werden diese von der Shell ersetzt. Dies ist eine ganz wichtige Tatsache, die zur Folge hat, daß nahezu jedes UNIX-Kommando als Dateiangabe immer eine (im Rahmen der BS-Parameter) beliebige Menge von Dateien als Parameter haben kann. Im Programm sind daher auch keine Systemaufrufe nötig, die auf die Verzeichnisinformation zugreifen; es wird lediglich eine Schleife benötigt, welche die einzelnen Dateien nacheinander bearbeitet. Metazeichen sind Zeichen mit erweiterter Bedeutung. Die Shell ersetzt die Metazeichen durch alle Dateinamen des aktuellen Verzeichnisses, die auf das Muster passen. Dabei können die Metazeichen beliebig oft an beliebiger Stelle im Dateinamen stehen (z. B.: *abc*def*). Es gibt folgende Metazeichen:

* Der Stern steht für eine beliebige Zeichenfolge - oder für überhaupt kein Zeichen. Dazu ein Beispiel:
"ab*" steht für alle Dateinamen, die mit "ab" anfangen, auch für "ab" selbst ("ab", "abc", "abcd", "abxyz", usw.).
? Das Fragezeichen steht für genau ein beliebiges Zeichen. Zum Beispiel:
"?bc" steht für alle Dateinamen mit 3 Zeichen, die auf "bc" enden ("abc", "bbc", "1bc", "vbc", "xbc", usw.), nicht jedoch für "bc".
[ ] Die eckige Klammer wird ersetzt durch eines der in der Klammer stehenden Zeichen. Auch ein Bereich ist möglich, z. B. [a-k] = [abcdefghijk]. Beispiel: "a[bcd]" wird ersetzt durch "ab", "ac" und "ad".
[! ] Die eckige Klammer mit Ausrufezeichen wird ersetzt durch eines der nicht in der Klammer stehenden Zeichen, zum Beispiel: "[!abc]" wird ersetzt durch ein beliebiges Zeichen außer a, b oder c.
\ Der Backslash hebt den Ersetzungsmechanismus für das folgende Zeichen auf. Beispiel: "ab\?cd" wird zu "ab?cd" - das Fragezeichen wird übernommen. Wichtig: Bei der Umleitung von Ein- und Ausgabe werden Metazeichen in den Dateinamen hinter dem Umleitungszeichen nicht ersetzt.

Beispiele für die Anwendung:
ls -l a* listet alle Dateien, die mit "a" anfangen

ls test? listet alle Dateien die mit "test" anfangen und 5 Zeichen lang sind ("test1", "test2", "testa")

ls /dev/tty1[1-9] listet alle Terminalbezeichnungen mit einer 1 in der Zehnerstelle ("tty11", "tty12", ... , "tty19")

Lebenswichtig:
Der * ist ein gefährliches Zeichen, Tippfehler könne zum Fiasko führen, wenn aus Versehen ein Leerzeichen zuviel getippt wird.
rm a* löscht beispielsweise alle Dateien, die mit "a" anfangen.
rm a * löscht dagegen erst die Datei "a" und dann alle Dateien im Verzeichnis.

Anmerkungen:

2.5 String-Ersetzungen (Quoting)

Um bestimmte Sonderzeichen (z. B. *, ?, [ ], Leerzeichen, Punkt) zu übergeben, ohne daß sie von der Shell durch Dateinamen ersetzt werden, werden Anführungszeichen verwendet, die auch ineinander geschachtelt werden können. Dabei haben die drei verschiedenen Anführungszeichen Doublequote ("), Quote (') und Backquote (`) unterschiedliche Bedeutung:

" " Keine Ersetzung der Metazeichen * ? [ ], jedoch Ersetzung von Shellvariablen (siehe unten) und Ersetzung durch die Ergebnisse von Kommandos (Backquote). Auch \ funktioniert weiterhin. Dazu ein Beispiel:
echo Der * wird hier durch alle Dateinamen ersetzt
echo "Der * wird hier nicht ersetzt"
' ' Das einfache Anführungszeichen unterdrückt jede Substitution. Zum Beispiel:
echo 'Weder * noch `pwd` werden ersetzt'
` ` Zwischen Backquote (Accent Grave) gesetzte Kommandos werden ausgeführt und das Ergebnis wird dann als Parameter übergeben (d. h. die Ausgabe des Kommandos landet als Parameter in der Kommandozeile). Dabei werden Zeilenwechsel zu Leerzeichen. Braucht dieses Kommando Parameter, tritt die normale Parameterersetzung in Kraft. Zum Beispiel:
echo "Aktuelles Verzeichnis: `pwd`"

Weil die verschiedenen Quotes manchmal schwer zu unterscheiden sind, wurde bei der bash eine weitere Möglichkeit eingeführt. Statt in Backquotes wird die Kommandofolge in $( ... ) eingeschlossen., z. B.:
echo "Aktuelles Verzeichnis: $(pwd)"

2.6 Bash - Die Linux-Shell

Die Bash (Bourne Again SHell) ist vollständig kompatibel zu der originalen Bourne-Shell aber sie hat etliche Erweiterungen erfahren. Sie ist bei fast allen Linux-Systemen die Standard-Shell.

Im Verlauf des ersten Aufrufs arbeitet jede Shell zunächst die Datei /etc/profile ab, in der der Systemverwalter für alle Anwender gültige Kommandos und Variablen eintragen kann. Daran anschließend sucht die Bash nach einer der folgenden Dateien im Heimatverzeichnis des Benutzers und führt die jenige aus, die zuerst gefunden wird:

Anders verhält es sich jedoch, wenn die Bash nicht als Login-Shell gestartet wird. Dann führt sie ausschließlich die Datei .bashrc aus.
Die Ausführung der Dateien läßt sich über zwei Kommandozeilen-Parameter steuern. Mit dem Parameter -noprofile veranlassen Sie, daß die Bash keine der oben genannten Startdateien ausführt, mit -norc erreichen Sie, daß die persönliche Konfigurationsdatei -/.bashrc ignoriert wird.

Der Prompt

Die Bash gibt einen Eingabeprompt aus, häufig in der Form Username:Pfad> . Der Prompt ist über die Umgebungsvariable PS1 konfigurierbar. Ein Prompt in der o. a. Form resultiert aus der Einstellung PS1=\u:\w\$ .
Um andere Einstellungen auszuprobieren, müssen Sie die Variable neu belegen. Innerhalb dieser neuen Einstellungen können Sie einige spezielle Konstante verwenden. Dies sind:

Edieren der Kommandozeile

Die Hauptaufgabe einer Shell ist die Entgegennahme und Ausführung von Kommandos. Zum Bearbeiten der aktuellen Kommandozeile stehen sowohl Emacs- als auch vi-kompatible Editiermodi zur Verfügung - voreingestellt ist der Emacs-Modus. Wenn Sie lieber im vi-Modus arbeiten, stellen Sie ihn durch den Befehl set -o vi ein, zurück in den Emacs-Modus geht's mit set -o emcas. Im Emacs-Modus gibt es unter anderem folgende Edierfunktionen:

Abhängig von der eingesetzten Terminalemulation können Sie Kombinationen mit der [Esc]-Taste oft auch mit der [Alt]-Taste nachgebilden. Statt also nacheinander [Esc] und [F] zu drücken, funktioniert meist auch die Kombination [Alt]+[F].

History-Mechanismus

In der Bash können Sie nicht nur die aktuelle Kommandozeile editieren, sie merkt sich auch alle einmal eingegebenen Befehle in einer Datei, der Kommandozeilen-History. Auch diese Datei befindet sich im Home-Directory des Benutzers und heißt .bash_history. Mit den Cursortasten auf/ab kann man in dieser Liste blättern. Darüber hinaus stehen folgende History-Befehle zur Verfügung:

Die Anzahl der Befehlszeilen wird mit der Variablen HISTSIZE eingestellt. Wächst die History darüber hinaus, verwirft die Bash die ältesten Zeilen.

Wichtige interne Kommandos

2.7 Schlußbemerkung

Es gibt zwei Möglichkeiten, einem UNIX-Kommando beim Aufruf Informationen mitzugeben: Für die letztere Möglichkeit gibt es das Kommando cat Datei (concatenate): Lesen der angegebenen Datei und Kopieren auf die Standardausgabe. Mit cat Datei | Kommando bekommt man den Dateininhalt in die Standardeingabe des Kommandos.

Auch für die Ausgabe gibt es zwei Möglichkeiten:

Wichtig werden diese Erkenntnisse erst bei Erstellen von Shell-Skripts, wo von allen geschilderten Möglichkeiten Gebrauch gemacht wird.

3 ed, reguläre Ausdrücke, grep und sort

Um Shellskripts und Programme zu erstellen, braucht man einen Texteditor Da auch nahezu alle UNIX-Steuerdateien reine Textdateien sind, sind auch hier Werkzeuge zur Textverarbeitung notwendig. Auch viele Shellskripts, welche die Ausgabe von Programmen verarbeiten, benötigen Text-Werkzeuge. Deshalb gehört die Manipulation von Texten eng zur Shell-Programmierung. Es wird an dieser Stelle aber nur auf die Editoren eingegangen, die üblicherweise in Shell-Skripts verwendet werden, ed und sed. Prinzipiell wäre es auch möglich, Bildschirmeditoren wie vi, emacs oder pico zu verwenden.

Der UNIX-Zeileneditor ed ist auf allen Systemen verfügbar, als Texthilfsmittel zum Suchen gibt es noch das Programm grep (Erweiterungen egrep, fgrep). Mit der Kommandozeilenversion des Editors ed, dem "sed", kann man die Manipulation von Texten automatisieren.

Zum Inhaltsverzeichnis Zum nächsten Abschnitt
Copyright © FH München, FB 04, Prof. Jürgen Plate