Entwurf:Linux:Shell:Bash

Aus ITWiki
Zur Navigation springen Zur Suche springen

1 Was ist eine Shell?

  • Das UNIX Shell Programm interpretiert Benutzerkommandos, die entweder direkt durch den Benutzer eingegeben oder aus einer Datei, dem Shellscript, gelesen werden.
  • Shellskripte werden interpretiert nicht kompiliert. Die Shell liest Zeile für Zeile und such nach solchen Befehlen auf dem System, während ein Compiler ein Programm in ein maschinenlesbares Format, eine ausführbare Datei, die dann in einem Shellscript genutzt werden kann.
  • Neben der Übermittlung von Kommandos an den Kernel ist die Hauptaufgabe einer Shell dem Benutzer eine Arbeitsumgebung zu stellen, die sich durch Konfigurationsdateien individuell konfigurieren lässt.

1.1 Allgemeine Shell-Funktionen

  • Die Shell ist die Eingabe-Schnittstelle zwischen Computer und Benutzer, welche bei normaler Systemkonfiguration nach dem erfolgreichen Login eines Benutzers gestartet wird, so dass man auf dieser Kommandozeile die Möglichkeit hat weitere Programme zu starten.
  • Die Shell könnte man somit aus Arbeitsumgebung bezeichnen, von wo aus der PC gesteuert werden kann bzw. Dateien bearbeitet werden können.
  • Es gibt unterschiedliche Arten von Shell’s, wer ein modernes Linux-Betriebssystem (Debian, Ubuntu…) installiert, landet meistens in der sogenannten “bash“, darauf gehe ich jedoch gleich genauer ein.
  • Die eingegebenen Texteingaben werden von der Shell interpretiert und ausgeführt, daher spricht man bei der Shell auch von einem Kommandozeileninterpreter (command-line interpreter, CLI).

1.1.1 Aufgaben einer Shell

  • In einem klassischen Unix-System (ohne grafische Oberfläche) greifen die Benutzer über Terminals auf das System zu. Auf diesen Terminals können nur Textzeichen dargestellt werden. Um dem Benutzer die Arbeit mit dem System effektiv möglich zu machen, gibt es die Shell. Die Shell wird dabei für drei Hauptaufgaben benutzt:* Interaktive Anwendung (Dialog)
    • Anwendungsspezifische Anpassung des Unix-Systemverhaltens (Belegen von Umgebungsvariablen)
    • Programmierung (Shell Scripting). Zu diesem Zweck stehen einige Mechanismen zur Verfügung, die aus Hochsprachen bekannt sind (Variablen, Datenströme, Funktionen usw.).
  • Ursprünglich handelte es sich dabei um ein relativ einfaches Programm, der Bourne Shell (wird oft auch Standard-Shell genannt).
  • Dies ist praktisch die Mutter aller Shells. Aus dieser entwickelten sich im Laufe der Zeit mehrere Varianten, die alle ihre eigenen Vor- und Nachteile mit sich bringen.
  • Da es unter Unix kein Problem darstellt den Kommandointerpreter auszutauschen, stehen auf den meisten Systemen mehrere dieser Shells zur Verfügung.

1.1.2 Vermittler zwischen Benutzer und Betriebssystem-Kern

  • Weil die direkte Kommunikation mit dem Betriebssystem-Kern für einen Benutzer viel zu komplex wäre, ist eine vereinfachte Benutzer-Schnittstelle erforderlich. Neben einer grafischen Schnittstelle wie dem X Window System wird diese Leistung vor allem von einer Shell bereitgestellt.

Datei:Grafik18.png

  • Der englische Ausdruck Shell, zu Deutsch etwa Schale oder Ummantlung, drückt diesen Sachverhalt bereits aus. Die Übersetzung oder Symbolisierung als Muschel hat dabei wohl mehr mit Spieltrieb und Anschaulichkeit als mit einem echten technischen Hintergrund zu tun.
  • Jedenfalls lässt sich eine Shell als eine Schicht zwischen Betriebssystem und Benutzer verstehen. Wir veranschaulichen dies in der folgenden Grafik:
  • Während Benutzer, die noch nicht häufig mit Shells in Berührung gekommen sind, den wartenden Eingabe-Prompt einer Shell als trist und abweisend, ja sogar als Hindernis empfinden mögen, wurden Shells doch mit der gegenteiligen Absicht entwickelt:
  • Sie sollten die tägliche Arbeit vereinfachen und erleichtern.

1.1.3 Interpretation der Kommandozeile

  • Die Hauptaufgabe einer Shell besteht darin, Kommandos entgegenzunehmen und das Betriebssystem um ihre Ausführung zu bitten. Das klingt einfach, stößt aber in der Praxis auf besondere Probleme und Anforderungen. Eine Shell hat daher zunächst eine Interpretation der Kommandozeile zu leisten.
  • Sie interpretiert einzelne Zeichen oder Worte der Eingabe und ersetzt sie ggf. durch neue Zeichen oder Worte. So stehen beispielsweise Variablen für bestimmte Zeichenketten und werden zunächst von der Shell durch ihren Inhalt ersetzt, bevor der Aufruf eines Kommando erfolgt.
  • Das gleiche gilt für Platzhalterzeichen wie * oder ?, die für die Angabe von Zeichenmustern verwendet werden können. Diese und zahlreiche weitere Mechanismen kann der Benutzer verwenden, um seine Kommandozeilen knapper und effektiver zu formulieren.

1.1.4 Kombination von Kommandos

  • Häufig ist es auch nicht nur ein einzelnes Kommando, das ausgeführt werden soll. Kommandos können beispielsweise so miteinander verbunden werden, dass die Ausgabe eines Kommandos zur Eingabe eines weiteren Kommandos wird.
  • Auch wiederholte oder bedingte Ausführungen in Form von Schleifen und wenn-dann-Konstrukten sind wünschenswert und werden daher von vielen Shells ermöglicht.
  • Schließlich möchte man häufig eine bestimmte wiederkehrende Folge von Kommandos ausführen lassen, die man daher in einer Datei niederschreibt und für die spätere Ausführung abspeichert.
  • Die Konfiguration eines Linux-Systems erfolgt häufig über solche Dateien, die als Shell-Skripte bezeichnet werden.

1.1.5 Bereitstellung einer persönlichen Arbeitsumgebung

  • Bei der täglichen Arbeit ist des weiteren die Konfiguration einer typischen Arbeitsumgebung von Bedeutung.
  • So möchte ein Benutzer beispielsweise meist mit einer passenden Spracheinstellung arbeiten, seinen Lieblingseditor zum Editieren von Texten verwenden, verwendeten Programmen Information über seine Vorlieben mitteilen, die Form seines Eingabe-Prompts ändern und vieles andere.
  • All dies lässt sich über sogenannte Shellvariablen konfigurieren, auf Wunsch auch dauerhaft in einer Konfigurationsdatei, damit die Einstellungen nicht bei jeder Anmeldung wiederholt werden müssen.
  • Eine Shell bietet ihrem Benutzer somit eine konfigurierbare, persönliche Umgebung, um typische Arbeitsabläufe möglichst komfortabel und fehlerfrei abwickeln zu können.

1.1.6 Begriffe

1.1.6.1 Konsole

In ihrer ursprünglichen Definition war die Konsole ein Terminal, mit dem der Systemoperator die Systemfunktionen steuern konnte. Heute wird der Begriff ebenfalls für “Terminalemulation” verwendet.

1.1.6.2 Terminal

Ein Terminal ist ein Computer, der den Zugriff auf einen entfernten Rechner erhält und die meisten Rechenoperationen somit nicht selber durchführt. (Terminal-PCs) Der Begriff “Terminal” wird heute jedoch auch als Abkürzung für “Terminalemulation” verwendet.

1.1.6.3 Terminalemulation

Mit einer Terminalemulation wird eine textorientierte Ein- und Ausgabeschnittstelle, ein Terminal emuliert, so dass man mehrere Instanzen eines Terminals auf dem Desktop zur Verfügung hat.

1.1.6.4 Virtuelle Konsole

Die meisten Linux-Systeme kommen standardmäßig mit einigen virtuellen Konsolen daher, welche man über [Strg] + [Alt] + [F1] bis [F6] erreichen kann. Mittels [Strg] + [Alt] + [F7] wird der Desktop und somit die graphischen Oberfläche wieder angezeigt.

1.2 Zugang zur Shell

Virtuelle Konsolen (tty) werden per Tastenkombination aufgerufen. Die Abkürzung tty stammt von dem englischen Wort Teletype und wurde historisch bedingt von Unix übernommen.

[Strg] + [Alt] + [F1]

… gelangt man in die erste der tty-Konsolen, in diesem Fall tty1. Hat man eine grafische Oberfläche (Desktop) installiert, so befindet sich dieser standardmäßig auf tty7.

  • Dieser Wert ist jedoch Variabel und kann umverlegt werden. Wer die Maus in einer solchen virtuellen Konsole verwenden will, sollte sich das Programm “gpm” anschauen / installieren.
  • Hat man sich einmal an die Desktop-Oberfläche gewöhnt, erscheinen einem diese virtuelle Konsolen meist wenig komfortabel, daher gibt es einige Programme (Terminalemulation) welche den Funktionsumfang erheblich erweitern.
  • Wenn man seine Standard-Terminal ändern möchte, kann man dies mit dem Alternativen-System bewerkstelligen… sudo update-alternatives --config x-terminal-emulator

1.2.1 KDE – Konsole

  • Das Programm unterstützt Tabs, Transparenz, verschiedenste Farbschema, unterschiedliche Schriften und man kann Profile anlegen.

Datei:Grafik20.png

1.2.2 Gnome – Terminal

  • Gnome-Nutzer verfügen über ein Programm mit dem Namen “Gnome-Terminal”.
  • Wer das Terminal immer Griffbereit haben möchte, sollte sich einmal “guake” anschauen…

Datei:Grafik31.png

1.2.3 Xterm

  • Xterm ist Bestandteil des X.org Projekts und war lange Zeit das Standardterminal für Linuxsysteme mit grafischer Oberfläche.

Datei:Grafik28.png

1.2.4 CLI Companion

  • Wer die Linux-Befehle noch lernen möchte oder komplizierte Befehle öfter benötigt, kann sich auch dieses Terminal anschauen.

Datei:Grafik23.png

1.2.5 Terminator

  • Terminator ist ein in Python geschriebener Terminal-Emulator für die Desktop-Umgebung GNOME, der es ermöglicht mehrere Terminals innerhalb eines Fensters bzw. einzelner Tabs zu benutzen und mittels Tastatur-Kürzeln zwischen diesen zu wechseln.
  • So kann man ohne Tabs und ohne weitere Terminalfenster mehrere Shells zur selben Zeit offen haben.”

Datei:Grafik33.png

  1. http://wiki.ubuntuusers.de/Terminator http://wiki.ubuntuusers.de/Terminator

1.3 Shell-Programme

1.3.1 Verfügbare Shells

  • So wie es verschiedene Programmierapachen und UNIX-Dialekte gibt, bietet ein UNIX-System üblicherweise verschieden Shells an.
  • Die Datei /etc/shells gibt einen Überblick über die bekannten Shells auf einem Linux-System:
cat /etc/shells
/bin/bash
/bin/sh
...
  • Die Standshell, ist in der Datei /etc/passwd festgelegt, wie beispielsweise die Zeile für den Benutzer dirkwagner:
dirkwagner:x:1000:100:Dirk Wagner:/home/dirkwagner:/bin/bash

1.3.2 Aufruf einer Shell

  • Eine Shell kann, wie jedes andere Programm, durch Aufruf seines Namens gestartet werden. Dabei werden alle Shells gefunen, die in der Path-Vsiable des angemdeten Benutzers enthalten sind.
  • Das System findet das Verzeichnis mit der passenden Programm über die PATH -Variable und da eine Shell ein normales Programm ist, wird es aufgerufen und ausgeführt.
  • Normalerweise wird ein neuer prompt angezeigt, da jede Shell Ihr eigenes Aussehen hat.
tcsh
$

Z-Shell starten

zsh

1.3.3 Wechsel der Standardshell

Standardmäßig die Z-Shell verwenden

sudo chsh -s /usr/bin/zsh `whoami`

1.3.4 Einfach- und Komfort-Shells

  • Komfort-Shells zeichnen sich durch komfortablere Funktionen zur interaktiven Bedienung aus, während die Einfach-Versionen üblicherweise für die Programmierung benutzt werden. Eine Shell kann wie jede andere Anwendung durch eine andere Shell ersetzt bzw. ausgetauscht werden.
  • Welche Shell der Anwender auch verwendet, jede Shell hat denselben Ursprung, die Mutter (oder auch der Vater) aller Shells ist die Bourne-Shell (sh), welche von S. R. Bourne 1978 entwickelt wurde und sich gleich als Standard-Shell im UNIX-Bereich durchgesetzt hat.
  • Einige Zeit später wurde für das Berkeley-UNIX-System eine weitere Shell namens C-Shell (csh) entwickelt, die erheblich mehr Komfort als die Bourne-Shell bot. Leider war die C-Shell bezüglich der Syntax gänzlich inkompatibel zur Bourne-Shell und glich mehr der Syntax der Programmiersprache C.
  • An diese beiden Shell-Varianten, der Bourne-Shell und der C-Shell, lehnten sich alle weiteren Shells an. Aus der Sparte der Bourne-Shell gingen als etwas bekanntere Vertreter die Shell-Varianten ksh (Korn-Shell), bash (Bourne-Again-Shell), zsh (Z-Shell), ash (A-Shell) und rbash bzw. rzsh (Restricted Shell) hervor. Aus der C-Shell-Abteilung hingegen trat nur noch tcsh (TC-Shell) als eine Erweiterung von csh in Erscheinung.
  • Die Bourne-Shell (sh) gibt es auf einem Linux-System eigentlich nicht und wird dort meistens vollständig von der bash (Bourne-Again-Shell) ersetzt. Wenn Sie also unter Linux die Bourne-Shell starten, führt dieser Aufruf direkt (als symbolischer Link) zur bash.
1.3.4.1 Einfache Shells
1.3.4.1.1 Bourne- / Standard-Shell (sh)
  • Kompakteste und einfachste Form
  • Mechanismen
    • Umlenkung der Ein- oder Ausgaben
    • Wildcards zur Abkürzung von Dateinamen
    • Shell-Variablen
    • Einen Satz interner Befehle zum Schreiben von Shell-Prozeduren
    • Neuere Versionen beherrschen auch das Job-Controlling
  • Kleinster gemeinsamer Nenner aller Shells
    • Für die Entwicklung von Shell-Skripten sollte man sich auf diese Shell beschränken, da sie auf praktisch allen Systemen zur Verfügung steht. So bleiben die Skripte portabel.
1.3.4.1.2 ksh (Korn-Shell)
  • Weiterentwicklung der Bourne-Shell
  • Hier wurden gegenüber der Bourne-Shell besonders interaktive Merkmale und auch einige Funktionalitäten der C-Shell hinzugefügt.
    • Editieren in der Befehlszeile
    • History-Funktionen
    • Ganzzahl-Arithmetik
    • Verbesserte Möglichkeiten zur Mustererkennung
    • Aliase
    • Job-Controlling
  • Die Korn-Shell ist seit UNIX.4 Bestandteil eines Standard-UNIX-Systems und gewöhnlich auch die Standard-Shell in der UNIX-Welt.
  • Wie auch bei der Bourne-Shell, wird der Aufruf unter Linux durch die Bash ersetzt.
  • Für den Fall der Fälle gibt es für Linux allerdings auch eine Version der Korn-Shell, eine Public Domain Korn-Shell (pdksh), welche den meisten Distributionen beiliegt.
1.3.4.1.3 C-Shell (csh)
  • Bietet ähnliche Annehmlichkeiten wie die Korn-Shell
  • Lehnt sich aber in der Syntax sehr stark an die Programmiersprache C an
  • Sollte nicht zur Shell-Programmierung benutzt werden, da sie an vielen Stellen nicht so reagiert, wie man es erwarten sollte
1.3.4.2 Komfort-Shells
1.3.4.2.1 Bourne-Again-Shell (bash)
  • Die Bash ist praktisch die Standard-Shell in der Linux-Welt und mittlerweile generell die am meisten benutzte Shell. Sie ist im Prinzip eine freie Nach-Implementierung der Bourne- und der Korn-Shell.* Ist voll abwärtskompatibel zur sh
  • Bietet von allen Shells die komfortabelsten Funktionen für das interaktive Arbeiten
  • Standard-Shell auf allen Linux-Systemen
  • Steht für die meisten anderen Unixen zur Verfügung
1.3.4.2.2 tcsh (TC-Shell)
  • Hierbei handelt es sich lediglich um eine verbesserte und ein wenig erweiterte Version der C-Shell (csh) mit voller Kompatibilität zu dieser.
  • Erweitert wurden allerdings nur die automatische Eingabevervollständigung, wie sie beim Betriebssystem »TENEX« (daher auch TC-Shell = TENEX C-Shell) anzutreffen war. TENEX ist allerdings wie die Dinosaurier bereits ausgestorben.* Verhält sich zur C-Shell wie die Bourne-Again-Shell zur Standard-Shell
  • Sie ist voll kompatibel, bietet aber zusätzliche Komfort-Funktionen
1.3.4.2.3 zsh (Z-Shell)
  • Die Z-Shell ist eine Erweiterung der Bourne-Shell, Korn-Shell und der Bash.
  • Die Zsh kann man als eine Zusammenstellung aller Verbesserungen und Features aus der bash, der csh und der tcsh betrachten.
  • Eine Shell mit derartiger Funktionalität müsste sich doch zu einer Art Standard-Shell etablieren!?
  • Dass dies nicht so ist und wahrscheinlich auch nie so sein wird, liegt wohl an der mangelnden Übersichtlichkeit ihrer Funktionalität.
  • Es stellt sich die Frage nach dem Sinn einer solchen Shell.

Features

  • eine frei programmierbare Wortvervollständigung (TAB-Completion)
  • die Möglichkeit, die History aus anderen – gleichzeitig laufenden – Shells zu nutzen
  • Rechtschreibüberprüfung
  • nahezu vollständige Kompatibilität zur bash, ksh und tcsh

Bei der Z-Shell kann hat man noch viel mehr Einstellungsmöglichkeiten, welche einem Anfangs ggf. überfordern können, daher auch hier meine “.zshrc“-Datei als Vorlage. -> zshrc

1.3.4.2.4 ash (A-Shell)
  • Bei der A-Shell handelt es sich um eine ähnliche Shell wie die Bourne-Shell, allerdings mit einigen Erweiterungen.
  • Die A-Shell gehört eher zu einer exotischeren Spezies und gilt auch nicht als sonderlich komfortabel.
  • Dass ich sie dennoch erwähne, liegt an der geringen Größe an Speicherplatz (60 KB), die sie benötigt (auch wenn dies heutzutage kein Thema mehr ist).
  • Dank dieser geringen Größe kann die ash überall dort verwendet werden, wo an Speicherplatz gespart werden muss.
  • Dies kann bspw. die Erstellung einer Notfall-Diskette sein, mit der man sein Linux-UNIX-System booten will.
  • Oder man ist auf der Suche nach einer Shell, die man in die Start-RAM-Disk beim initrd-Verfahren laden will.
  • Selbst in einer ziemlich abgeriegelten Umgebung (bspw. mit chroot) kommt man im lokalen Zugriffsmodus mit der ash sehr weit.
  • Ist darauf optimiert, dass möglichst viele externe Kommandos durch die internen Funktionen der Shell ersetzt werden.
  • Dadurch ist das System auch dann bedienbar, wenn wesentliche externe Programme fehlen
  • Ist vor allem nützlich für die System-Recovery
1.3.4.2.5 rbash, rzsh (Restricted Shell)
  • Hierbei handelt es sich um »erweiterte« Shell-Varianten der Bash bzw. der zsh.
  • Die Erweiterung bezieht sich allerdings hier nicht auf den Funktionsumfang, sondern eher auf gewisse Einschränkungen des Benutzers in seinen Rechten und Möglichkeiten.
  • Diese Einschränkungen können vom Systemadministrator für jeden Benutzer spezifisch gesetzt werden. Dies ist besonders bei fremden Zugriffen von außen recht sinnvoll.
1.3.4.2.6 rsh und ssh

Obwohl rsh (Remote Shell) und ssh (Secure Shell) keine Shells wie bspw. die Korn-Shell bzw. die Bash sind, sollen diese hier trotzdem kurz erwähnt werden, weil es gern und häufig zu Missverständnissen bei Anfängern führt. * Hierbei handelt es sich nicht – wie bei den eben vorgestellten Shells – um eine Benutzerschnittstelle zum System.

  • Beide Kommandos werden dazu verwendet, auf einem anderen System (etwa Ihrem Webhoster) übliche Shell-Kommandos (remote) auszuführen.
  • Die ssh ist der Sicherheit zuliebe der rsh vorzuziehen, da die ssh z. B. ein erweitertes Authentifizierungsverfahren benutzt und die Passwörter vom Login sowie die komplette Datenübertragung verschlüsselt.
  • Dadurch ist ein Abhorchen der Daten nicht möglich.

1.3.5 Verbreitung der Shells

Es gibt zwei Linien der Shell-Programmierung – die Bourne-Shell- und die C-Shell-Familie. In der heutigen Shell-Welt wird vorzugsweise die Bourne-Shell-Familie verwendet, genauer: *

    • UNIX die Korn-Shell als Standard-Shell
    • Linux benutzt die Bash als Standard-Shell
    • Bei FreeBSD hingegen scheiden sich häufig die Geister
      • für Anwender sh
      • für Superuser häufig die csh als Standard gesetzt

Die C-Shell-Familie verliert allerdings aufgrund einiger Schwächen (bspw. fehlen einige wichtige Programmier-Konstrukte) immer mehr an Boden gegenüber der Bourne-Familie.

  • Somit arbeiten wir im weiteren mit der Bash und gehen auf einige Besonderheiten der Bourne- Shell ein, weil die viele Boot-Scripts (init-Scripts) mit ihr erstellt werden.
  • Die Korn-Shell, weil diese quasi die Standard-Shell unter UNIX ist und immer noch unzählige Scripts hierfür geschrieben werden.

Die Bash ist die Standard-Shell unter Linux und betrifft damit die meisten Benutzer.

Die Unterschiede dieser drei Shells nicht sehr groß, wenn es also einen besonderen Unterschied zwischen dieser oder jener Shell gibt, so wird darauf hingewiesen.

2 Bourne-again shell (bash)

Datei:Grafik26.png

  • Die Bourne-again shell (bash) ist eine freie Unix-Shell und Teil des GNU-Projekts.
  • Die Bash wird von der Free Software Foundation entwickelt und ist die Standardshell des GNU-Betriebssystems HURD.

2.1 Der Name

Datei:Grafik27.png

  • Der Name ist absichtlich mehrdeutig und bedeutet unter anderem „wiedergeborene (born again) Shell“, „wieder einmal (eine) Bourne-Shell“ oder auch aus dem Englischen to bash (schlagen, kritisieren, schlecht machen).

2.2 Geschichte

  • Die Bash ist die mit Abstand beliebteste Shell unter Linux. Zum Teil mag das an ihrer frühen Verfügbarkeit liegen, während freie Implementierungen der Tcsh und Ksh für Linux erst nachzogen.
  • Entscheidend scheint der enthaltene Funktionsumfang, den die anderen Shells nicht annähernd mit sich bringen und die Konformität mit dem IEEE POSIX P1003.2/ISO 9945.2 Shell und Tools Standard. Hierin ist auch der Grund für die Unterschiede zur Bourne Shell (sh bzw. bsh) zu sehen.
  • Auch wenn der Name Bash die enge Verwandtschaft zur Bsh nahe legt, so hat die GNU Bash gleich drei Vorfahren. Die eben genannte Bsh, die Csh und die Ksh. Von jedem erbte die Bash die Konstrukte, die sich aus der Erfahrung heraus als besonders nützlich erwiesen hatten.* Geschrieben wurde bash 1987 von Brian Fox und wurde 1990 von Chet Ramey übernommen.
    • Version 3 erschien am 27. Juli 2004.
    • Version 4 erschien am 20. Februar 2009 und brachte einige Neuerungen mit sich. Darunter sind eine neue Ausgabeumleitung, assoziative Arrays und eine neue Wildcard (**).

2.3 Verbreitung

Die Bash (/bin/bash) ist die Standard-Shell sowohl auf den meisten Linux-Systemen als auch unter Mac OS X und wurde auf fast alle Unix-Systeme portiert. Sie ist heute auf vielen unixoiden Systemen die Standard-Shell. Für Windows existieren zahlreiche Portierungen. Die bekanntesten sind:* Cygwin

  • MSYS (Minimal SYStem)
  • Microsoft Windows Services for UNIX

2.4 Fähigkeiten der Bash

  • Das GNU Projekt (GNU's Not UNIX) stellt Werkzeuge für die Administration UNIX-ähnlicher Systeme zur Verfügung, die freie Software und kompatibel zu UNIX Standards sind. Bash ist ein Sh-kompatible Shell, die nützliche Funktionen der Korn shell (ksh) und der C shell (csh) übernommen hat.
  • Die Bash wurde geschrieben um konform zum 'IEEE POSIX P1003.2/ISO 9945.2 Shell and Tools standard' zu sein, sie st größtenteils kompatibel zur originalen Bourne-Shell (sh), im Funktionsumfang jedoch erheblich erweitert und Verbesserungen bei der Programmierung und der interaktiven Nutzung.
  • Die Shell ist weitgehend kompatibel zur Bourne-Shell (sh) und beherrscht zusätzlich sowohl die meisten Funktionen der Korn-Shell (ksh) als auch Teile der C-Shell (csh)-Syntax, wie zum Beispiel die „Command-History“, die $RANDOM-Variable und die POSIX-Form der Command-Substitution $(...) wurden übernommen.
  • Auch wurde sie um Funktionen wie z. B. der Ganzzahlarithmetik ohne die Ausführung externer Prozesse und Vereinfachung der I/O-Umleitungen erweitert.
  • Bash bietet außerdem die Möglichkeit, in der Datei ~/.bashrc eigene Einstellungen wie Designs zu speichern.
  • Vor allem beherrscht sie einen Großteil der Fähigkeiten der ksh und versteht auch Teile der Syntax der csh, wie zum Beispiel die Command-History, den Directory-Stack, die $RANDOM-Variable und die POSIX-Form der Command-Substitution $(…).

Daneben sind auch eine ganze Reihe eigene Erweiterungen implementiert.* Editieren der Kommandozeile

  • Kommandozeilenspeicher (unlimited size command history)
  • Steuerung von Prozessen (job control)
  • Eingabevervollständigung
  • Geringe Fehler in der Eingabe können automatisch korrigiert werden.
  • Die Eingabeaufforderung ist konfigurierbar.
  • Berechnungen analog zu C werden unterstützt (integer arithmetic in any base from two to sixty-four)
  • Aliase und Funktionen (shell functions and aliases)
  • Felder (indexed arrays of unlimited size)
  • Die Bash kann die meisten Sh-Skripte ohne Anpassungen ausführen
2.4.1 Bedingungen
  • Bedingte Ausdrücke können durch das [[ compound command sowie dem internen test und [ - Kommando.
  • Ausdrücke können unär oder binär sein. Unäre Ausdrücke werden oft benutzt, um den Status einer Datei zu ermitteln.
  • Es wird nur eine Objekt benötigt (z.B. eine Datei), um solche Operationen durchzuführen.
  • Außerdem gibt es Zeichenketten- und nummerische Vergleichsoperatoren; dies sind binär und erfordern zwei Objekte für die Operation.
  • Wenn das FILE-Argument von der Form /dev/fd/N ist, wird der file descriptor N geprüft.
  • Ist FILE /dev/stdin, /dev/stdout oder /dev/stderr, werden die file descriptors 0, 1 or 2 geprüft.
2.4.2 Shell Arithmetik
  • Die Shell kann arithmetische Ausdrücke als Shell-Ausdruck oder mit dem internen Befehl let prüfen.
  • Die Prüfung wird als fixed-width integers ohne overflow-Prüfung durchgeführt. Divisionen durch Null werden gefangen (trapped) und als Fehler markiert.
  • Operatoren und ihre Rangfolge und Assoziativität entsprechen der Programmiersprache C.
2.4.3 Aliase
  • Aliase erlauben es eine Zeichenkette zu ersetzten, wenn sie das erste Wort eines einfachen Kommandos sind.
  • Die Shell hält eine Liste von Aliasen, die mit alias und unalias gesetzt bzw. gelöscht werden können.
  • Die Bash liest immer mindestens eine ganz Zeile ein, bevor eines der Kommandos ausgeführt wird.
  • Aliase werden expandiert, wenn ein Kommando gelesen wird, nicht, wenn es ausgeführt wird. Dadurch haben Aliasdefinition in der gleichen Zeile keinen Effekt, bis die nächste Zeile eingelesen wird.
  • Kommandos die in der gleichen Zeile auf eine Aliasdefinition folgen, haben keinen Einfluss auf den neuen Alias.
  • Aliase werden expandiert wenn eine Funktionsdefinition gelesen und nicht, wenn sie ausgeführt wird, weil eine Funktionsdefinition selber eine compound command ist.
  • Folglich sind Aliase, die innerhalb einer Funktion definiert werden erst verfügbar, wenn die Funktion ausgeführt wurde.
2.4.4 Arrays
  • Die Bash stellt eindimensionale Array-Variablen zur Verfügung
  • Jede Variable kann als Array genutzt werden. Das interne Kommando declare deklariert explizit ein Array.
  • Es gibt weder ein Limit für die maximale Größe eines Arrays, noch die Anforderung, dass Mitglieder indiziert oder kontinuierlich sein müssen.
  • Der Array-Index beginnt bei Null.
2.4.5 Verzeichnisstapel
  • Der Verzeichnisstapel (directory stack) ist eine Liste der zuletzt besuchten Verzeichnisse.
  • Das interne Kommando pushd fügt dieser Liste ein Verzeichnis hinzu und wechselt in das Verzeichnis. Das interne Kommando popd entfernt das angegebene Verzeichnis von dem Stapel und wechselt in das entfernte Verzeichnis.
  • Der Inhat des Verzeichnisstapels kann mit dem internen Kommando dirs oder durch auslesen der Variable DIRSTACK angezeigt werden.
  • Weiter Informationen zur Arbeit mit diesem Mechanismus können der Infopage der Bash entnommen werden.
2.4.6 Eingabeaufforderung (Prompt)
  • Der Eingabeaufforderung (Prompt) der Bash kann umfangreich konfiguriert werden.
2.4.7 Shell Syntax
  • Wenn die Eingabe nicht auskommentiert wurde, liest die Shell sie und teilt sie in Wörter und Operatoren mit Hilfe von Maskierungsregeln, um die Bedeutung jedes Eingabezeichens festzulegen.
  • Danach werden diese Wörter und Operatoren in Kommandos und andere Konstruktionen übersetzt, die einen Rückgabewert (exit status) erzeugen, um die Verarbeitung zu inspizieren.

Dieses fork-and-exec Schema wird nur angewandt, nachdem die Shell die Eingabe wie folgt analysiert hat:* Die Shell liest ihre Eingabe aus einer Datei, einem string oder vom Benutzerterminal.

  • Die Eingabe nah den Maskierungsregeln wird in Wörter und Operatoren zerlegt. Diese Token werden durch Metazeichen getrennte. Die Alias-Ersetzung wird durchgeführt.
  • Die Shell parst (Analyse und Substitution) die Token in einfache und zusammengesetzte Kommandos.
  • Die Bash führt verschiedene Ersetzungen (expansions) durch und zerlegt die expandierten Token in Dateiliste, Kommandos und Argumente.
  • Wenn notwendig, wird eine Umleitung durchgeführt und die Umleitungs-Operatoren und -Ziele werden aus der Argumenteinliste entfernt.
  • Die Kommandos werden ausgeführt.
  • Gegebenenfalls wartet die Shell bis das Kommandos beendet wurden und sammelt ihren Rückgabewert.
2.4.8 Shell Kommandos
  • Ein einfaches Shell Kommando wie touch file1 file2 file3 besteht aus dem Kommando selber, gefolgt von Argumenten, getrennt durch Leerzeichen.
  • Komplexere Shell-Kommandos werden auf verschiedene Weise aus einfachen zusammengesetzt: Durch Pipelines, die die Ausgabe eines Kommandos an das nächste weiterreicht, durch Schleifen, Bedingungen oder andere Gruppierungen. Ein paar Beispiele
ls | more
gunzip file.tar.gz | tar xvf - 
2.4.9 Shell Funktionen
  • Shell Funktionen sind ein Weg Kommandos zu gruppieren, um sie später durch einen Namen aufzurufen.
  • Sie werden ausgeführt, wie ein „normales“ Kommando. Wenn der Name einer Shell-Funktion als einfaches Kommando genutzt wird, wird die Liste der hinterlegten Kommandos ausgeführt.
  • Shell-Funktionen werden im aktuellen Shell-Kontext ausgeführt; es wird kein neuer Prozess erzeugt um sie zu interpretieren.
2.4.10 Shell Parameter
  • Eine Shell-Variable ist eine Entität, die einen Wert speichert. Das kann ein Name, eine Zahl oder ein spezieller Wert sein. Für die Shell eine Variable ist in Parameter, der einen Namen speichert. Eine Variable hat einen Wert und null oder mehrere Attribute. Variablen werden mit dem eingebauten Kommando declare erzeugt.
  • Wird kein Wert übergeben enthält sie einen null string. Variablen können nur mit dem internen unset gelöscht werden.
2.4.11 Shell Expansionen
  • Folgende Shell-Expansionen werden durchgeführt, nachdem die Kommandozeile in Token zerlegt wurde:* Auflösung von Aliasen
    • Klammer-Expansion
    • Tilde-Expansion
    • Parameter- und Variablenexpansion
    • Kommandosubstitution
    • Arithmetische Substitution
    • Wortaufteilung
    • Pfadnamensexpansion
2.4.12 Umleitungen
  • Bevor ein Kommando ausgeführt wird, werden eventuell seine Ein- und Ausgabe durch spezielle Notationen, die durch die Shell interpretiert werden, umgeleitet.
  • Umleitungen können auch genutzt werden, um Dateien für die aktuelle Shell-Umgebung zu öffnen oder zu schließen.

3 Start und beenden

  • Startdateien sind Skripte die gelesen und ausgeführt werden, wenn die Bash startet. Der Folgende Abschnitt beschreibt verschiedene Wege die Shell zu starten und welche Startdateien dabei gelesen werden.

3.1 Aufruf

  • Zusätzlich zu den kurzen Kommandozeilenoptionen, die über interne Kommandos konfiguriert werden können, gibt es diverse lange Optionen die genutzt werden können.
  • In den folgenden Abschnitten werden einige wichtige Optionen vorgestellt. Eine vollständige Liste der Optionen ist den Bash info pages zu entnehmen (Bash features->Invoking Bash).

3.1.1 Optionen beim Start

Datei:Grafik6.png

  • Die Bash ist ein Programm und nahezu jedes Programm unter Unix kann durch Optionen auf der Kommandozeile gesteuert werden.
  • So kennt auch die Bash eine Reihe von Optionen, deren wichtigste vorgestellt sein sollen:
3.1.1.1 -c Kommandofolge

Die Bash liest und startet die Kommandos aus "Kommandofolge", welche als eine einzelne Zeichenkette anzugeben ist.

Alles, was der Zeichenkette folgt, wird als Argument dem letzten Kommando der Kommandofolge übergeben:

bash date -u
date: /bin/date: cannot execute binary file
bash -c date -u
Sam Jul 8 10:16:17 MEST 2000

Wird der Bash ein Argument übergeben, das keine Option ist, so interpretiert sie das Argument als Datei, die die Kommandos enthält. date ist keine Datei, die Kommandos beinhaltet, also beschwert sich die Bash. Die Bash in Verbindung mit -c arbeitet als nicht-interaktive Shell.

3.1.1.2 -r, --restricted

Die Bash arbeitet als »Restricted« Shell.

3.1.1.3 -i

Die Bash arbeitet als interaktive Shell, d.h. die Standardein- und -ausgabe sind mit einem Terminal verbunden.

3.1.1.4 --login

Die Bash arbeitet wie eine Login-Shell, d.h. alle Konfigurationsdateien werden eingelesen. Eine Login-Shell hat nichts mit der Anzeige einer Login-Aufforderung zu tun, sie nennt sich nur so, weil die einzulesenden Dateien zumeist einmalig beim Start einer Sitzung geladen werden.

3.1.1.5 --noprofile

Unterdrückt das Einlesen der profile-Dateien.

3.1.1.6 --norc

Unterdrückt das Einlesen der ~/.bashrc«.

3.1.1.7 --posix

Die Bash verhält sich wie eine POSIX-Shell. Sie liest hierzu einzig die in der Umgebungsvariablen ENV angegebene Datei ein. Wird die Bash über den Link sh gerufen, startet sie ebenso im POSIX-Modus.

3.1.1.8 --rcfile Datei

Eine nicht-interaktive Bash liest die angegebene Datei ein (anstatt ~/.bashrc)

3.1.1.9 -v, --verbose
  • Schaltet die erweiterte Ausgabe ein. Sinnvoll ist diese Option in der Testphase von Shellskripten, um jede Zeile, die ausgeführt wird, angezeigt zu bekommen. Als weitere Optionen können zahlreiche Flags bereits beim Start der Bash aktiviert werden.
  • Da dies auch bei laufender Shell geschehen kann - und dies der gebräuchliche Weg ist, werden die Flags erst im Zusammenhang mit dem builtin-Kommando set behandeln.

3.2 Verhalten beim Start

3.2.1 Interaktiv / Nicht-Interaktiv

Datei:Grafik15.png

  • Ist eine Shell mit der Standardein- und Standardausgabe (mit einer (virtuellen) Konsole) verbunden, so bezeichnet man sie als interaktive Shell.
  • Die interaktive Bash teilt sich wiederum ein in die Login Bash, die Nicht-Login Bash und die Restricted Bash.
  • Alle drei unterscheiden sich im Einlesen von Initialisierungsdateien; Eine Restricted Bash schränkt die Befugnisse des Benutzers ein.
  • Wenn die Bash startet, entscheidet sie anhand des Ergebnisses von tty -s, ob es sich um eine interaktive Shell handelt (dann ist der Rückgabewert des Kommandos "0").
  • Eine Login-Shell führt die Kommandos aus der Datei /etc/profile und aus der ersten gefundenen Datei * ~/.bash_profile
    • ~/.bash_login oder
    • ~/.profile
  • aus, sofern die Dateien existieren, lesbar sind und die Bash nicht mit der Option --noprofile gestartet wurde.
  • Eine Nicht-Login Bash liest die Datei ~/.bashrc. Mit der Option --rcfile Diese_Datei« kann eine alternative Ressourcen-Datei benannt und mit --norc das Einlesen unterdrückt werden.
  • Eine Nicht-Interaktive Shell (typisch sind Shellskripte) wertet einzig die Umgebungsvariable BASH_ENV aus. Enthält sie den vollständigen Pfad zu einer Datei, so wird deren Inhalt gelesen und ausgeführt.

3.2.2 Bash als Login-Shell

Datei:Grafik7.png

  • In den meisten Konfigurationen, die die Distributoren ausliefern, dürfte die Bash als Login-Shell voreingestellt sein.
  • Ist dem nicht so, kann der Benutzer mit Hilfe des Kommandos chsh die Bash in die Passwortdatei eintragen.
  • Dies funktioniert jedoch nur, wenn die Bash in der Datei /etc/shells eingetragen ist.
  • Ist dem nicht so, könnte einem entweder der Administrator aus der Patsche helfen, indem er den Eintrag in obiger Datei ergänzt oder man baut in einem der Startup-Skripte seiner bisherigen Login-Shell den Aufruf exec /usr/bin/bash --login ein.
  • Bei dem zuletzt beschriebenen Vorgehen sollten Sie sicher stellen, dass das Skript mit dem Aufruf nicht auch von der Login-Bash ausgeführt wird, sonst hängt die Shell in einer Schleife mit Reinitialisierungen fest.
  • Am besten fragen Sie im Skript ab, ob der Name des aktuell ausgeführten Programms die Bash ist:
...
 case "$0" in
    *bash)
       ;;
    *)
       test -x /usr/bin/bash && exec /usr/bin/bash –-login
       ;;
 esac
 ...

3.2.3 Interaktive Login-Shell

Interaktiv bedeutet, dass Kommandos eingegeben werden können. Login-Shell meint, dass die Shell nach der Anmeldung vom Anmeldeprozess (getty oder ein Desktopmanager) oder mit ‑‑login gestartet wurde.

Eingelesene Dateien* /etc/profile

  • ~/.bash_profile, ~/.bash_login or ~/.profile: die erste existierende lesbare Datei wird gelesen
  • ~/.bash_logout nach der Abmeldung

Fehlermeldungen werden ausgegeben, wenn Konfigurationsdateien existieren aber nicht lesbar sind. Wenn eine Datei nicht existiert, such die Bash nach der nächsten.

3.2.4 Interaktive Nicht-Login-Shell

  • Eine Nicht-Login-Shell wird gestartet, wenn der Anwender bereits am System angemeldet ist. Beispielsweise, wenn von einer grafischen Oberfläche aus ein Terminal-Fenster gestartet wird.
  • Eingelesene Dateien:* ~/.bashrc
  • Diese Datei wird üblicher Weise in ~/.bash_profile referenziert:
if [ -f ~/.bashrc ] ; then . ~/.bashrc ; fi

3.2.5 Nicht-interaktiver Aufruf

Alle Skripte nutzen Nicht-interaktive Shells. Sie wurden geschrieben um bestimmte Aufgaben zu erledigen und können nicht angewiesen werden andere Aufgaben zu erledigen als für welche sie programmiert wurden.

Eingelesene Dateien:* Festgelegt in der Umgebungsvariable BASH_ENV

Die PATH-Variable wird nicht für die Suche nach Dateien genutzt, daher sollten Dateien ggf. mit absolutem Pfad referenziert werden.

3.2.6 Aufruf mit dem Kommando sh

Die Bash versucht sich zu verhalten wie das hitorische Bourne sh Programm und auch konform zum POSIX Standard zu sein.

Eingelesene Dateien* /etc/profile

  • ~/.profile

Bei interaktivem Aufruf kann die Umgebungsvariable ENV auf eine zusätzliche Startdatei zeigen.

3.2.7 POSIX Modus

  • Diese Option kann mit dem internen Befehl set gesetzt werden,
set -o posix 
  • oder durch den Aufruf der Bash mit der Option ‑‑posix.
  • Die Bash wird sich so kompatibel wie möglich zu dem POSIX standard for shells zu verhalten.
  • Das setzten der Umgebungsvariable POSIXLY_CORRECT aktiviert den Posix-Modus ebenfalls.
  • Eingelesene Dateien:* Festgelegt in der Umgebungsvariable ENV

3.2.8 Entfernter Aufruf

Dateien, die gelesen werden, wenn der Aufruf über rshd erfolgt.* ~/.bashrc

3.2.8.1 Die r-tools sollten nicht mehr genutzt werden!

Die Nutzung veralteter Tools wie rlogin, telnet, rsh und rcp birgt große Risiken.

Sie sind unsicher, weil sie Passwörter und Daten unverschlüsselt übertragen und keinen Schutz gegen Abhören, Manipulation oder Übernahme der Verbindung bieten.

Entfernte Shells sollten mit Werkzeugen wie ssh aufgerufen werden.

3.2.9 UID nicht gleich EUID

Wenn sich User-ID (UID) und Effektive User-ID (EUID) unterscheiden, werden keine Startdateien gelesen.

Dieser Fall kann eintreten, wenn ein Skript mit gesetztem setuid-Bit gestartet wird.

3.2.10 Eingeschränkte Shell

Datei:Grafik8.png

  • Wird die Bash als rsh, bash, mit --restricted oder -r aufgerufen, so startet sie als eingeschränkte (restricted) Shell.
  • Der Anwender unterliegt dann verschiedenen Einschränkungen.
3.2.10.1 Achtung
  • Das Einrichten einer abgesicherten Umgebung für Benutzer ist eine anspruchsvolle Aufgabe. Das Einrichten einer einschränken Shell kann dabei nur eine von vielen Maßnahmen darstellen. In einigen Fällen mag es sinnvoller erscheinen, eine chroot-Umgebung einzurichten.
  • Stellen Sie sicher, dass die exportierte PATH-Variable keine Verzeichnisse enthält, die eine Shell enthalten. Es ist sinnvoll, zu diesem Zweck ein eigenes Binary-Verzeichnis mit »unkritischen« Kommandos (analog zu FTP) anzulegen und einzig dieses in PATH aufzunehmen.
  • Es muss sicher gestellt sein, das erlaubte Programme nicht in der Lage sind die von der restricted shell gesetzten Einschränkungen zu umgehen. Achten Sie dabei besonders auf Programme, die es erlauben eine Shell zu starten (z. B. Editoren).
3.2.10.2 Einschränkungen
  • Der eingebaute cd Befehl ist abgeschaltet
cd..
bash: cd: restricted* Setzten, ändern und löschen der Variablen SHELL, PATH, ENV oder BASH_ENV ist nicht möglich. 
export PATH=./:$PATH
bash: PATH: readonly variable
unset SHELL
bash: unset: SHELL: cannot unset: readonly variable* Kommandoaufrufe mit absolutem Pfad sind nicht möglich. Nur Kommandos die in den Pfaden der Variable PATH enthalten sind können aufgerufen werden. Dies kann nicht mit dem eingebauten Befehl command umgangen werden.
/bin/ls
bash: /bin/ls: restricted: cannot specify `/' in command names* Dateinamen, die einen Slash „/“ enhaten sind nicht erlaubt (auch nicht mit dem include-Operator „.“:
. /etc/profile
bash:.: /etc/profile: restricted* Umleitungen (>, >|, ><, >&, &> und >>) sind abgeschaltet.
ls > ls.log
bash: ls.log: restricted: cannot redirect output* Import-Funkionen beim Start sind abgeschaltet, SHELLOPTS wird ignoriert beim Start. 
  • Der eingebaute Befehl exec ist deaktiviert, um es nicht zu ermöglichen, die Shell durch einen anderen Prozess zu ersetzen und dadurch die Privilegien zu erhöhen.
  • builtin-Kommandos zu aktivieren bzw. zu deaktivieren (also ihren momentanen Status zu ändern)
  • Abschalten des restricted-Modus ist nicht möglich.
  • Der interne Befehl hash akzeptiert kein „/“ (slash).
  • Die ‑f und ‑d Optionen des eingebauten Befehls sind deaktiviert.

Wenn ein Kommando ein Shellscript ist, schaltet rbash alle Beschränkungen für die ausführende Shell ab.

3.3 Beenden der Bash

3.3.1 exit

exit [n]

Beendet die Shell mit dem Exit-Status N. Wird N weggelassen wird, ist der Exit-Status der des letzten ausgeführten Befehls. Alternativ kann auch die Tastenkombination [Strg] + [d] verwendet werden.

3.3.2 Logout

logout [n]

Beendet die Shell mit dem Exit-Status N. Wird N weggelassen wird, ist der Exit-Status der des letzten ausgeführten Befehls. Liefert einen Fehler, wenn es nicht in einer Login-Shell ausgeführt wird.

Ob die Bash nun mittels exit oder logout verlassen oder sie mit einem Signal (Ausnahme: KILL) zum Ende gebracht wird, so wird sie abschließend noch das ihr auferlegte Pensum erledigen.

Handelt es sich um eine Login-Shell, so lässt sich die Bash sogar vor ihrem Ende zu einem letzten Groß-Reine-Machen motivieren.

3.3.3 ~/.bash_logout

Alle Aktionen, die die Shell vor ihrem Ableben noch erledigen soll, sind hierzu in eine Datei .bash_logout zu schreiben.

3.3.4 Ersetzen des aktuellen Prozesses

Ein Ersetzen des Programms durch ein anderes mittels exec kommt einer Beendigung gleich.

exec wird Im Kapitel ‚Eingebauten Kommandos‘ behandelt.

3.4 Zusammenfassung

Die Bash ist die GNU Shell, kompatibel mit der Bourne Shell. Sie enthält viele nützliche Funktionen anderer Shells. Wenn die Shell startet, liest sie ihre Konfigurationsdateien ein.

Die wichtigsten sind: * /etc/profile

  • ~/.bash_profile
  • ~/.bashrc

Die Bash verhält sich anders, wenn sie interaktiv, restricted oder POSIX-Kompatibel gestartet wird.

Shell-Kommandos können in drei Gruppen eingeteilt werden* Shell-Funktionen

  • Interne Befehle (shell built-ins)
  • Im Dateisystem enthaltene Kommandos

Die Bash unterstützt zusätzliche interne Kommandos, die in der ursprünglichen Bourne-Shell nicht enthalten waren.

Shellskripte bestehen aus solchen Kommandos verbunden durch Shell-Syntax. Sie werden Zeile für Zeile gelesen und ausgeführt und sollten eine logische Struktur besitzen.

3.5 Übungen

1. Wo ist das bash Programm auf dem System abgelegt?

2. Benutzen Sie die --version Option um die Version Ihrer bash herauszufinden.

3. Welche Konfigurationsdateien werden eingelesen, wenn Sie sich grafisch anmelden und dort ein Terminal-Fenster öffnen?

4. Sind die folgenden Shells interaktiv? Sind es Login-Shells?

  • Eine Shell, die von einer grafischen Oberfläche aus gestartet wird, beispielsweise dur starten eines Terminal-Programms.
  • Eine Shell die nach dem Aufruf von ssh localhost aufgerufen wird.
  • Eine Shell, die nach einer Anmeldung an einer Textkonsole aufgerufen wird.
  • Eine Shell, die durch den Befehl xterm & aufgerufen wird.
  • Eine Shell die durch das Skript mysystem.sh geöffnet wird.
  • Eine Shell von einem entfernten Host, an dem Sie sich anmelden müssen, weil Sie SSH benutzen oder sich mit SSH keys authentifizieren.

5. Können Sie erklären, warum die Bash nicht beendet wird, wenn Sie Ctrl+C drücken?

6. Lassen Sie den Inhalt des directory stack anzeigen.

7. Löschen Sie die Variable für den primären Prompt.

8. Setzen Sie jetzt PS1 so, dass der Prompt den aktuellen Pfad enthält. Beispielsweise indem Sie der Datei ~/.bashrc folfende Zeile hinzufügen: export PS1="\u@\h \w> "

9. Probieren Sie auch andere Konfiguration der Eingabeaufforderung.

10. Lassen Sie sich die hashed commands für die aktuelle Sitzung anzeigen.

11. Wie viele Prozesse laufen aktuell auf Ihrem System? Benutzen Sie ps und wc. Die erste Zeile der Ausgabe von ps ist kein Prozess!

12. Wie können Sie sich den system hostname anzeigen lassen? Nur den Namen, nicht mehr!

4 Initialisierung und Konfiguration

4.1 Konfigurationsoptionen

set und shopts sind zwei wichtige eingebaute Kommandos der Bash, mit Hilfe derer sich das Verhalten der Shell auf vielfache Art und Weise konfigurieren lässt.

Eigentlich eher in den Initialisierungsdateien angewendet, lassen sich beide Kommandos auch interaktiv verwenden.

Datei:Grafik19.png

Beginnen wir mit set und betrachten zunächst den Status der von einer typischen Bash gesetzten Variablen:

set -o

allexport       off
braceexpand     on
errexit         off
hashall         on
histexpand      on
keyword         off
monitor         on
noclobber       off
noexec          off
noglob          off
notify          off
nounset         off
onecmd          off
physical        off
privileged      on
verbose         off
xtrace          off
history         on
ignoreeof       on
interactive-comments    on
posix           off
emacs           on
vi              off

Für jede der in obigem Beispiel aufgeführten Variablen kennt set eine "kurze" Option, um diese zu aktivieren (on) bzw. zu deaktivieren.

Aus der Fülle der Optionen genügt die Kenntnis der schon erwähnten Option -o. Ohne Angabe weiterer Argumente werden alle Variablen (die mit set manipuliert werden können) angezeigt.

Folgt dieser der (die) Namen einer (mehrerer) Variablen, so wird (werden) diese aktiviert. Zum Deaktivieren dient die Option +o in Verbindung mit einem Variablennamen.

4.1.1 Wichtige Variablen

allexport Alle Variablen der Shell werden exportiert sobald sie erstellt oder verändert werden.
braceexpand Schaltet die Klammernexpansion an bzw. ab.
hashall Ist diese Option gesetzt, werden Kommandos, die die Bash bereits einmal gesucht hat (PATH), mit vollständigem Pfad in einer Hashtabelle gespeichert. Dadurch wird ein folgender Aufruf desselben Kommandos erheblich beschleunigt.
histexpand Die Möglichkeit des Zugriffs auf zuvor eingegebene Kommandozeilen mittels eines vorangestellten ! wird ein- bzw. ausgeschalten.
monitor Durch Deaktivieren wird die Statusmeldung eines im Hintergrund gestarteten Prozesses (»Job«) unterdrückt. In ähnlichem Zusammenhang steht notify, womit der Status eines beendeten Hintergrundprozesses ohne Verzögerung auf dem Bildschirm landet.
noclobber Wird diese Variable aktiviert, lassen sich existierende Dateien nicht mittels Ein- und Ausgabeumleitung überschreiben:

touch testdatei; ls > testdatei

# Alles im grünen Bereich...
set +o noclobber' 
ls > testdatei
bash: testdatei: cannot overwrite existing file

Wird die Ausgabeumleitung mittels ">|" realisiert, schützt auch "noclobber" nicht vorm Überschreiben.

noexec Kommandos werden zwar gelesen, aber nicht ausgeführt. Diese Variable wird gern in der Testphase während der Erstellung von Shellskripten gesetzt.
noglob Die Pfadnamensexpansion kann ein- und ausgeschalten werden.
onecmd Die Shell wird nach Ausführung des ersten Kommandos beendet. Eine denkbare Anwendung wäre in Verbindung mit Postfächern, wo ein Benutzer sich "von außen" anmeldet, durch ein Skript ein Kommando, das die Mail überträgt, gestartet wird und anschließend der Benutzer automatisch ausgeloggt wird. Damit erhält ein Benutzer niemals vollwertigen Zugang zum Rechner.
posix Die Bash verhält sich nach IEEE POSIX P1003.2/ISO 9945.2 Shell und Tools Standard.
emacs, vi Schaltet das Verhalten des Kommandozeileneditors auf den jeweiligen Modus.

4.2 Optionen anzeigen

  • Einige Bash Optionen wurden schon erwähnt. In diesem Abschnitt sollen die Konfigurationsmöglichkeiten nun etwas genauer betrachtet werden.
  • Die Option -o von set zeigt alle Shell-Optionen an
$ set -o
allexport               off
braceexpand             on
emacs                   on
errexit                 off
hashall                 on
histexpand              on
history                 on
ignoreeof               off
interactive-comments    on
keyword                 off
monitor                 on
noclobber               off
noexec                  off
noglob                  off
nolog                   off
notify                  off
nounset                 off
onecmd                  off
physical                off
posix                   off
privileged              off
verbose                 off
vi                      off
xtrace                  off
  • Vollständige Beschreibung aller Optionen: Info pages, Abschnitt Shell Built-in Commands->The Set Built-in
  • Viele der Optionen besitzen eine kurze Aufruf-Option. Beispielsweise kann die Option xtrace auch mit set -x gesetzt werden.

4.3 Optionen ändern

  • Shell-Optionen können entweder beim Aufruf der Shell als Schalter angegeben werden oder während der Ausführung aktiviert werden. Die umfasst auch die Konfigurationsdateien.
  • Das folgende Kommando führt ein Skript im POSIX-Kompatiblen Modus aus:
bash --posix script.sh
  • Um die aktuelle Umgebung zeitweise zu ändern oder innerhalb eines Skriptes sollte der Befehl set genutzt werden.
  • Optionen werden mit einen Minus-Zeichen „-“ (dash) ein- und mit einen Plus-Zeichen „+“ ausgeschaltet:
set -o noclobber
touch test
date > test
bash: test: cannot overwrite existing file
set +o noclobber
date > test
  • Das Beispiel zeigt die Option noclobber, die verhindert, dass bestehende Dateien durch Umleitungen überschrieben werden.
  • Das Gleiche funktioniert mit einer kurzen Option z.B. -u, die ein unset von Variablen als Fehler behandelt und nicht interaktive Shells beendet, wenn ein solcher Fehler auftritt.
echo $VAR
set -u
echo $VAR
bash: VAR: unbound variable
  • Diese Option ist auch nützlich, um eine fehlerhafte Zuweisung zu einer Variablen zu erkennen. Der gleiche Fehler würde auftreten, wenn einer explizit als integer deklarierten Variable eine Zeichenkette zugewiesen würde.
  • Das nächste Beispiel zeigt die Option noglob, die verhindert, dass Shell-Sonderzeichen expandiert werden:
set -o noglob
touch *
ls -l *
-rw-rw-r--    1 willy    willy          0 Feb 27 13:37 *
4.3.1 Aktivieren einer Variable mit set -o
set -o allexport noclobber
set -o | egrep 'allexport|noclobber' 
allexport       on
noglob          on
4.3.2 Deaktivieren einer Variable mit set +o
set +o allexport
set -o | egrep 'allexport|noclobber' 
allexport       off
noglob          off

4.4 Optionen seit Bash 2.0

Vorsicht abweichende Optionen* Um eine Shellvariable zu aktivieren, ist die Option -s zu verwenden.

  • Dem entgegen schaltet -u die Wirkung der Variablen ab.
  • So verhilft "-p" zu einem Blick auf den Status der von "shopt" verwalteten Variablen:
$ shopt -p
shopt -u cdable_vars
shopt -u cdspell
shopt -u checkhash
shopt -s checkwinsize
shopt -s cmdhist
shopt -u dotglob
shopt -u execfail
shopt -s expand_aliases
shopt -u extglob
shopt -u histreedit
shopt -u histappend
shopt -u histverify
shopt -s hostcomplete
shopt -u huponexit
shopt -s interactive_comments
shopt -u lithist
shopt -u mailwarn
shopt -u no_empty_cmd_completion
shopt -u nocaseglob
shopt -u nullglob
shopt -s progcomp
shopt -s promptvars
shopt -u restricted_shell
shopt -u shift_verbose
shopt -s sourcepath
shopt -u xpg_echo

4.4.1 Wichtiger Variablen

cdspell Ist die Option gesetzt, vermag die Shell geringe Tippfehler in Pfadangaben zu korrigieren:

cd /us/x11R6

bash: cd: /us/x11r6/: Datei oder Verzeichnis nicht gefunden
shopt -s cdspell
cd /us/x11R6
/usr/X11R6/
lithist Bei der Eingabe von Kommandos, die über mehrere Zeilen gehen, speichert die Bash diese normalerweise ohne die enthaltenen Zeilenumbrüche in der History.

Wenn Sie diese Variable auf "on" setzen, bleiben diese Zeilenumbrüche auch in der History enthalten.

dotglob Bei der Pfadnamensexpansion werden Dateien, deren Namen mit einem Punkt beginnen, nur berücksichtigt, wenn diese Variable gesetzt ist:
  1. Im Beispiel existiert im aktuellen Verzeichnis einzig eine Datei ".foo"#
ls *
ls: *: Datei oder Verzeichnis nicht gefunden

shopt -s dotglob
ls *
.foo
mailwarn Ist diese Variable gesetzt, überprüft die Bash die Mailboxen auf neue Nachrichten und gibt ggf. eine Mitteilung aus. Die Option korrespondiert mit den Variablen MAIL und MAILPATH.
interactive_comments Ist die Option gesetzt, gilt alles, was auf der Kommandozeile einem Doppelkreuz # folgt, als Kommentar
nocaseglob Ist diese Option gesetzt, spielt Groß- und Kleinschreibung von Dateinamen für die Bash keine Rolle:

ls.F*

ls:.F*: Datei oder Verzeichnis nicht gefunden

shopt -s nocaseglob
ls -F*
.foo
restricted_shell Diese Option ist gesetzt, wenn die Bash im restricted Modus gestartet wurde; sie kann nicht geändert werden.

Die weiteren Variablen sind wohl selten von Nutzen, es sei denn, jemand wünscht gezielt bestimmte Expansionsmechanismen zu deaktivieren.

4.5 Konfigurationsdateien der Bash

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 diejenige aus, die zuerst gefunden wird:# .bash_profile

  1. .bash_login
  2. .bash_logout
  3. .profile

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ässt sich über zwei Kommandozeilen-Parameter steuern.

Mit dem Parameter -noprofile veranlassen Sie, dass die Bash keine der oben genannten Startdateien ausführt, mit -norc erreichen Sie, dass die persönliche Konfigurationsdatei ~/.bashrc ignoriert wird.

4.5.1 Die Initialisierungsdateien

Handelt es sich bei einer Login-Shell um die Bash oder die Korn Shell, sucht diese als erstes nach der Datei /etc/profile.

Diese wird vom Systemadministrator meist dazu genutzt, allen Nutzern eine auf das System zugeschnittene Umgebung zu gewähren. Häufig enthalten sind: * Prüfen der Mailbox auf neue Nachrichten

  • Ausgabe der "Nachricht des Tages" (Inhalt der Datei /etc/motd)
  • Setzen wichtiger Aliase
  • Vorbelegen wichtiger Variablen

Gerade in Umgebungen, wo Benutzer sowohl auf die Bash, als auch auf die ksh zurück greifen, sollte die /etc/profile sorgfältig erstellt werden, da gewisse Unterschiede zu ungewollten Effekten führen können.

Zwei weitere Konfigurationsdateien sollen rein Bash-spezifische Einstellungen ermöglichen.

Die Dateien .bash_profile und .bash_login im Heimatverzeichnis eines Nutzers werden, falls sie existieren, in beschriebener Reihenfolge behandelt.

Um kompatibel zur kommerziellen Unix-Shell sh zu sein, liest die Bash die beiden Dateien allerdings nicht aus, wenn auf sie unter dem Namen sh (ein Link auf /bin/bash) zugegriffen wird.

Anschließend wertet die Bash noch eine eventuell vorhandene Datei .profile aus, die, wiederum im Heimatverzeichnis eines Nutzers gelegen, erster Ansatzpunkt für den Nutzer darstellt, persönliche Einstellungen zur Umgebung vorzunehmen.

Im Falle einer interaktiven, Nicht-Login Shell versucht die Bash Befehle aus einer Datei ~/.bashrc abzuarbeiten. Schließlich hällt eine nicht-interaktive Bash (Shellskripte) nach einer in der Umgebungsvariablen BASH_ENV gespeicherten Datei Ausschau.

In allen erwähnten Dateien können dieselben Befehle stehen (z.B. Kommandos, Alias- und Variablendefinitionen,...). Abhängig von der konkreten Linuxdistribution werden ggf. aus den beschriebenen Standarddateien heraus weitere Startup-Dateien eingelesen.

4.5.2 Ändern der Shell Konfigurationsdateien

Wenn die Konfigurationsdateien der Shell verändert werden, muss sich der Anwender neu verbinden, damit die Änderungen wirksam werden.

Die veränderte Datei kann auch mit dem Befehl source eingebunden werden, dadurch werden die Änderungen auch für die aktuelle Shell wirksam.

4.5.2.1 Unterschiedliche Prompts für unterschiedlich Benutzer

Gegen Text austauschen

Die meisten Stellscripte werden in einer privaten Umgebung ausgeführt. Variablen werden nicht vom Subprozess übernommen, wenn sie nicht von der übergeordneten Shell exportiert wurden.

Eine Datei mit Shell-Befehlen per source einzubinden ist ein Weg, um Änderungen oder Variablen in der aktuellen Umgebung wirksam werden zu lassen. Dieses Beispiel zeigt auch, die Nutzung unterschiedlicher Prompts für unterschiedliche Anwender. Der Befehl source resourcefile tut das gleiche, wie . resourcefile.

Sollte unklar sein, aus welcher Datei eine Einstellung stammt, nutzen das Kommando echo um die Scripte zu debuggen.

echo "Now executing .bash_profile.."

echo "Now setting PS1 in .bashrc:"
export PS1="[some value]"
echo "PS1 is now set to $PS1"

4.6 Systemweite Konfiguration

4.6.1 /etc/profile

Wird die Bash interaktiv mit der Option ‑‑login oder als sh aufgerufen wird, liest die die Datei /etc/profile. Dort werden üblicher Weise die Shellvariablen PATH, USER, MAIL, HOSTNAME and HISTSIZE gesetzt.

Auf einigen Systemen wird der Wert für die umask in /etc/profile konfiguriert, auf anderen enthält diese Datei einen Zeiger auf andere Konfigurationsdateien:* /etc/inputrc, die systemweite Konfigurationsdatie für Readline, in der line bell-style eingestellt werden kann.

  • Das Verzeichnis /etc/profile.d, dass Dateien für das systemweite Verhalten bestimmter Programme enthält.

Einstellungen, die für alle Benutzer gelten sollen, werden in dieser Datei festgelegt. Sie könnte wie folgt aussehen:

  1. /etc/profile
  1. System wide environment and startup programs, for login setup

PATH=$PATH:/usr/X11R6/bin

  1. No core files by default

ulimit -S -c 0 > /dev/null 2>&1

USER="`id -un`" LOGNAME=$USER MAIL="/var/spool/mail/$USER"

HOSTNAME=`/bin/hostname` HISTSIZE=1000

  1. Keyboard, bell, display style: the readline config file:

if [ -z "$INPUTRC" -a ! -f "$HOME/.inputrc" ]; then INPUTRC=/etc/inputrc fi

PS1="\u@\h \W"

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC PS1

  1. Source initialization files for specific programs (ls, vim, less, ...)

for i in /etc/profile.d/*.sh ; do if [ -r "$i" ]; then $i fi done

  1. Settings for program initialization

source /etc/java.conf export NPX_PLUGIN_PATH="$JRE_HOME/plugin/ns4plugin/:/usr/lib/netscape/plugins" PAGER="/usr/bin/less" unset i

Diese Datei setzt einige grundlegende Umgebungsvariablen und einige Variablen, die für Java und Java-Anwendungen benötigt werden.

Die Bash-Quellen enthalten einige Beispieldateien die, genau wie das Beispiel oben, angepasst werden müssen, damit sie in Ihrer Umgebung funktionieren.

4.6.2 /etc/bashrc

Auf Systemen die unterschiedliche Shell-Typen anbieten, ist es besser Bash-spezifische Einstellungen in einer eigenen Datei festzulegen, da /etc/profile is bereits von anderen Shells, wie z.B. der Bourne Shell.

Fehler durch Shells, die die Bash-Syntax nicht verstehen, werden vehindet, indem die Konfiguation auf verschiedene Dateien für die verschiedenen Shells aufgeteilt wird.

In solchen Fällen kann die Benutzerdatei ~/.bashrc die systemweite /etc/bashrc einbinden, um die Shell-Initialisierung beim Login zu starten.

Es kann auch sein, dass /etc/profile nur die Shell-Umgebung und Einstellungen für den Start von Programmen und /etc/bashrc die systemweite Definition von Shell-Funktionen und Aliasen enthält. Eventuell wird auch /etc/bashrc in /etc/profile oder in einer benutzerspezifischen Initialisierungsdatei referenziert.

Unter /usr/share/doc/bash-version/startup-files finden sich Beispieldateien. Der folgende Teil stammt aus Bash-Dokumention:

alias ll='ls -l' alias dir='ls -ba' alias c='clear' alias ls='ls --color'

alias mroe='more' alias pdw='pwd' alias sl='ls --color'

pskill() { local pid

pid=$(ps -ax | grep $1 | grep -v grep | gawk '{ print $1 }') echo -n "killing $1 (process $pid)..." kill -9 $pid echo "slaughtered." }

Neben allgemeinen Aliasen enthält sie einige zur Korrektur von Tippfehler und eine Stellfunktion um Prozesse zu beenden.

4.7 Benutzerspezifische Konfiguration

Wenn diese Dateien im Home-Verzeichnis nicht vorhanden sind, können sie bei Bedarf erstellt werden.

4.7.1 ~/.bash_profile

Das ist die bevorzugte Konfigurationsdatei um die Benutzerumgebung einzurichten. In dieser Datei können Benutzer zusätzliche Optionen setzen oder Standardeinstellungen ändern:

cat .bash_profile
#################################################################
#                                                               #
#   .bash_profile file                                          #
#                                                               #
#   Executed from the bash shell when you log in.               #
#                                                               #
#################################################################

source ~/.bashrc
source ~/.bash_login
case "$OS" in
IRIX)
stty sane dec
stty erase
;;
#  SunOS)
#    stty erase
#    ;;
*)
stty sane
;;
esac
  • Dieser Benutzer hat die Rückschitt-Taste für unterschiedliche Betriebssysteme konfiguriert. Außerdem werden die eigenen .bashrc and .bash_login eingelesen.

4.7.2 ~/.bash_login

Diese Datei enthält Einstellungen, die normalerweise nur ausgeführt werden sollen, wenn sich der Benutzer anmeldet.

Im Beispiel wird die umask gesetzt, eine Liste der angemeldeten Benutzer und ein Kalender des aktuellen Monats ausgegeben:

  1. #
  2. Bash_login file #
  3. #
  4. commands to perform from the bash shell at login time #
  5. (sourced from .bash_profile) #
  6. #
  7. file protection

umask 002 # all to me, read to group and others

  1. miscellaneous

w cal `date +"%m"` `date +"%Y"`

Diese Datei wird zuerst gelesen, wenn die Datei ~/.bash_profile fehlt.

4.7.3 ~/.profile

Diese Datei wird gelesen, wenn die Dateien ~/.bash_profile und ~/.bash_login fehlen.

Sie kann die alle Einstellungen enthalten, die andere Shell auch verstehen; andere Shell verstehen unter Umständen die Syntax der Bash nicht.

4.7.4 ~/.bashrc

Heute werden häufig Nicht-Login-Shells benutzt, weil die Shell oft von einer grafischen Oberfläche durch ein Terminal-Fenster gestartet wird. Der Benutzer hat sich bereits grafisch am System angemeldet.

Die Bash sucht in diesem Fall nach ~/.bashrc, so dass sie in den Dateien referenziert wird, die beim Login eingelesen werden, damit die gleichen Einstellungen nicht mehrmals vorgenommen werden müssen.

In dieser .bashrc wurden einige Aliase definiert und Variablen für Programme gesetzt, nachdem die systemweite /etc/bashrc eingelesen wurde:

cat .bashrc

  1. /home/dirkwagner/.bashrc
  1. Source global definitions

if [ -f /etc/bashrc ]; then /etc/bashrc

fi

  1. shell options

set -o noclobber

  1. my shell variables

export PS1="\[\033[1;44m\]\u \w\[\033[0m\] " export PATH="$PATH:~/bin:~/scripts"

  1. my aliases

alias cdrecord='cdrecord -dev 0,0,0 -speed=8' alias ss='ssh octarine' alias ll='ls -la'

  1. mozilla fix

MOZILLA_FIVE_HOME=/usr/lib/mozilla LD_LIBRARY_PATH=/usr/lib/mozilla:/usr/lib/mozilla/plugins MOZ_DIST_BIN=/usr/lib/mozilla MOZ_PROGRAM=/usr/lib/mozilla/mozilla-bin export MOZILLA_FIVE_HOME LD_LIBRARY_PATH MOZ_DIST_BIN MOZ_PROGRAM

  1. font fix

alias xt='xterm -bg black -fg white &'

  1. BitchX settings

export IRCNAME="frnk"

4.7.5 ~/.bash_logout

Diese Datei enthält Anweisungen die vor der Abmeldung durchgeführt werden.

Im Beispiel wird das Terminal vor der Abmeldung geleert, aber auch Aufgaben, wie das Kopieren von Dateien, sind denkbar.

cat .bash_logout

  1. Bash_logout file #
  2. #
  3. commands to perform from the bash shell at logout time #

clear

4.8 Shell-Variablen

Variablen lassen sich auf verschiedene Art und Weise in konkrete Kategorien einordnen. Im Sinne der Shell beeinflussen sie deren Verhalten. Diese, in dem Zusammenhang als Umgebungsvariablen bezeichneten Variablen unterteilen sich wiederum in Variablen, die die Bash setzt und solche, die sie nur benutzt.

Variablen besitzen einen Sichtbarkeitsbereich und lassen sich in lokale und globale Variablen unterteilen.

4.8.1 Wichtige Variablen, die die Bash setzt

BASH Vollständiger Pfad der aktuellen Bash. In dem Zusammenhang gibt BASH_VERSION die Programmversion preis und BASH_VERSINFO ist ein Feld (5 Einträge) mit genaueren Angaben
BASH_ENV Enthält eine Initialisierungsdatei, die beim Start von Subshells für Skripte aufgerufen wird.
HISTFILE Datei, wo die History bei Ende einer interaktiven Bash gespeichert wird. Wie viele Zeilen diese enthalten kann, steht in HISTFILESIZE.
HISTSIZE Anzahl maximal gespeicherter Kommandos im History -Speicher
HOME Enthält das Heimverzeichnis des Benutzers.
HOSTNAME Rechnername ohne Domain.
HOSTTYPE Architekturtyp des Rechners
IFS Der Internal Field Separator enthält die Trennzeichen, anhand derer die Bash nach erfolgter Expansion die Eingabe in Worte zerlegt.
MAIL Enthält den Namen einer Mailbox; wenn dort neue Nachrichten eintreffen, wird der Benutzer benachrichtigt. Allerdings nur, falls MAILPATH nicht gesetzt ist. Letztere Variable enthält die Pfade zu mehreren Dateien, die überwacht werden sollen.
OLDPWD Vorhergehendes Arbeitsverzeichnis; bspw. könnten Sie mit "cd -" in dieses wechseln.
OPTARG und OPTIND Wert und Index des letzten, von getopts bearbeiteten Argument.
OSTYPE Typ des Betriebssystems.
PATH Enthält die Suchpfade, in denen die Bash nach Kommandos sucht. Beachten Sie die Reihenfolge der Suche - bevor die Bash PATH betrachtet, durchsucht sie zunächst die Aliasse, die Funktionen und die eingebauten Kommandos nach dem Namen.
PPID Prozessnummer des Bash-Vorgänger-Prozesses
PS1...PS2 Siehe Prompts
PWD Aktuelles Arbeitsverzeichnis.
RANDOM Erzeugt eine Zufallszahl bei jedem Zugriff.
REPLY Variable, die die zuletzt vom Kommando read gelesene Zeile enthält.
SHELLOPTS Liste der Shelloptionen.
TMOUT Ist dieser Wert gesetzt, beendet sich die Bash, wenn innerhalb der angegebenen Zeitspanne (in Sekunden) keine Eingabe erfolgte. Hiermit kann auf einfache Art und Weise ein Auto-Logout realisiert werden.

4.8.2 Lokale und globale Variablen

Lokale Variablen sind nur in der Shell ihrer Definition sichtbar, d.h. in keinem innerhalb der Shell gestarteten Kommando (Ausnahme: builtin-Kommando, aber dieses "ist" ja die Shell selbst).

Plausibler vorzustellen ist der Sachverhalt innerhalb der Prozesshierarchie. Eine lokale Variable ist somit nur im Prozess ihrer Definition sichtbar, nicht jedoch in den daraus abgeleiteten Prozessen.

Im Gegensatz hierzu ist eine globale Variable ab dem Prozess ihrer Definition sichtbar; also in allen abgeleiteten, nicht jedoch in den übergeordneten Prozessen.

4.8.3 Definition lokaler Variablen

localvar="eine lokale Variable"

Lokale Variablen sind nur in der Shell ihrer Definition sichtbar

bash

echo $localvar

exit
echo $localvar
eine lokale Variable

4.8.4 Definition globaler Variablen

Jede Variable kann zu einer globalen Variable erklärt werden. Globale Variablen sind ab (aber nicht in einer Vorgängershell) der Shell ihrer Definition sichtbar

export localvar

bash
echo $localvar

eine lokale Variable

Eine letzte Herangehensweise in die Thematik der Variablen betrachtet deren Eigenschaften, ob sie bspw. änderbar sind oder einen bestimmten Typ besitzen (vergleiche Variablen).

4.8.5 Umgebungsvariablen anzeigen (printenv)

printenv zeigt alle oder einzelne Umgebungsvariablen für Programme.

Werden kein Variable in der Kommandozeile übergeben, werden alle Umgebungsvariablen ausgegeben.

printenv [Variable ...]

4.8.6 Eingabeaufforderungen (Prompt)

Arbeitet man interaktiv mit der Bash, zeigt die Shell ihre Bereitschaft zur Entgegennahme von Eingaben anhand eines Prompts ("Eingabeaufforderung") an. Vier Prompts werden unterschieden, wobei zwei die Eingabeaufforderung symbolisieren.

4.8.6.1 Primärer Prompt

Dieses zeigt an, dass die Bash eine neue Eingabe erwartet.

4.8.6.2 Sekundärer Prompt

Wurde die Eingabe eines Kommandos nicht vollendet und dennoch versucht, die Zeile durch "ENTER" abzuschließen, zeigt die Bash dieses "Fortsetzungsprompt".

Typische Situationen, die das Prompt hervorzaubern, sind: der Abschluss einer Zeile mit dem Backslash \, eine vergessene schließende Klammer oder Anführungszeichen:

(echo "Das Zeilenende kam eindeutig [ENTER]

> zu zei\[Enter]
> tig"[Enter]
>)[Enter]
das Zeilenende kam eindeutig
zu zeitig

4.8.6.3 Dritter Prompt

(nur bash) Dieses Prompt erscheint bei der interaktiven Arbeit mit dem builtin-Kommando select und ist über die Shellvariable "$PS3" konfigurierbar.

4.8.6.4 Vierter Prompt

(nur bash) Im Debug-Modus (set -x) wird dieses Prompt ("$PS4") vor jeder expandierten Kommandozeile ausgegeben.

Der Modus ist hilfreich, um zu sehen, wie die Bash eine Eingabe expandiert, bevor sie diese an das Kommando zur Ausführung übergibt:

testvar=text

echo $testvar
text
set -x
echo $testvar
+ echo text
text

Das Aussehen der beiden ersten Prompts wird durch den Inhalt der beiden Variablen "$PS1" (primäres Prompt) und "$PS2" (sekundäres Prompt) bestimmt und kann in begrenztem Umfang geändert werden.

4.8.6.5 Üblichen Inhalt der Variablen

echo $PS1

\u@\h:>
echo $PS2
>

"$PS1" enthält offensichtlich Sonderzeichen, die zum Nutzerkennzeichen \u bzw. zum Rechnernamen \h expandieren. Wichtige mögliche Zeichen sind:

\e Leitet eine Escape-Sequenz ein
\d Datum
\h Rechnername bis zum ersten enthaltenen Punkt
\s Shellname
\t Aktuelle Zeit
\u Nutzername
\w Aktueller Pfadname
\$ "#", falls UID=0 und "$" sonst
\nnn Die Zahl nnn wird als ASCII-Code interpretiert. So lassen sich auch nichtdruckbare Zeichen und Escape-Sequenzen ("\033" entspricht "\e")verwenden.

Das folgende Beispiel ändert den primären Prompt, so dass Nutzername, aktueller Pfad und das Datum angezeigt werden:

PS1="\u:\w:\d>"

user:~:Wed Jun 7>

Vielleicht wäre etwas Farbe für das Prompt noch besser? Mit Escape-Sequenzen lässt sich auch dieses realisieren:

PS1="\[\033[01;31m\]$PS1\[\033[m\]"

4.8.7 Lokalisierung

Zum Anzeigen der Einstellungen

locale

Sie können Umgebungsvariablen setzen, um die Ländereinstellungen des Systems zu steuern. Sie können diese Variablen systemweit oder für jede Sitzung definieren.

Unter AIX und Linux befinden sich diese Variablen in /etc/environment:

Variable Description
LC_ALL Überschreibt alle LC_*-Umgebungsvariablen mit dem angegebenen Wert
LC_CTYPE Zeichenklassifikation und Wechsel zwischen Groß- und Kleinschreibung
LC_COLLATE Sortierreihenfolge
LC_TIME Datums- und Zeitformate
LC_NUMERIC Nicht-monetäre numerische Formate
LC_MONETARY Monetäre Formate
LC_MESSAGES Formate von Informations- und Diagnosenachrichten und interaktiven Antworten
LC_PAPER Papierformat
LC_NAME Namensformate
LC_ADDRESS Adressformate und Standortinformationen
LC_TELEPHONE Telefonnummernformate
LC_MEASUREMENT Maßeinheiten (metrisch oder anders)
LC_IDENTIFICATION Metadaten über die länderspezifischen Informationen
LANG Der Standardwert, der verwendet wird, wenn entweder 'LC_ALL' nicht festgelegt oder ein gültiger Wert für 'LC_**' nicht bestimmt wird.
NLSPATH Mit Begrenzern versehene Pfadliste für die Suche nach Nachrichtenkatalogen
TZ Zeitzone

4.8.8 Übungen

Für diese Übung sollte die man-Page von useradd zu Rate gezogen werden, weil das Verzeichnis /etc/skel genutzt wird, um Standard Shell-Konfigurationsdateien zu speichern.

Diese werden beim Anlegen eines neuen Anwenders in dessen Home-Verzeichnis kopiert.

Zunächst einige allgemeine Übungen zum setzen und Anzeigen von Variablen: # Erstellen Sie drei Variablen, VAR1, VAR2 and VAR3; initialisieren Sie diese so, dass sie die Werte "dreizehn", "13" und "Herzlichen Glückwunsch" enthalten.

  1. Lassen Sie sich den Inhalt der drei Variablen anzeigen.
  2. Sind diese Variablen lokale oder globale Variablen?
  3. Löschen Sie die VAR3.
  4. Können Sie die übrigen zwei Variablen in einem neuen Terminal-Fenster sehen?
  5. Verändern Sie die Datei /etc/profile so, dass alle Anwender begrüßt werden. Testen Sie das.
  6. Setzen Sie für den User root den Prompt mit "Vorsicht!! root arbeitet in \w", bevorzugt in einer grellen Farbe wie rot oder pink oder im invertierten Modus.
  7. Stellen Sie sicher, dass neu angelegte Benutzer auch einen schönen personalisierten Prompt erhalten, der sie darüber informiert, auf welchem System und in welchem Verzeichnis sie gerade arbeiten. Testen Sie diese Einstellungen, indem Sie sich mit diesem Zugang anmelden.
  8. Schreiben Sie ein Skript, dass zwei integer Variablen akzeptiert. Das Skript soll die Fläche eines Rechtecks mit diesen Abmessungen errechnen. Es sollte ausreichend kommentiert sein und eine ansehnliche Ausgabe erzeugen.

5 Interaktive Bash

Eine Interaktive Shell liest und schreibt grundsätzlich auf das Terminal des Benutzers: Ein- und Ausgabe sind mit dem Terminal verbunden.

Der interaktive Modus der Bash wird gestartet, wenn das 'bash'-Kommando ohne non-option argument gestartet wird, also ohne auszuführendes Kommando oder Skript.

5.1 Kommandosyntax

5.1.1 Aufbau eines Kommandos

UNIX-Kommandos haben folgenden Aufbau:

Befehlsname Optionen Dateiname(n)

Die Bestandteile eines Kommandos müssen durch Whitespaces voneinander getrennt werden, damit sie von der Shell erkannt werden. Die Eingabe wird durch Drücken der RETURN-Taste abgeschlossen.

5.1.2 Kommandobeschreibung

mount [-lhV]

mount -a [-fFnrsvw] [-t vfstype] [-O optlist]
mount [-fnrsvw] [-o option[,option]...]  device|dir
mount [-fnrsvw] [-t vfstype] [-o options] device dir

In den Manual-Pages werden Kommandos oft in Kurzform beschrieben.

Unter einander werden die verschiedenen Anwendungsformen des Befehls mit den möglichen Optionen und Parameter aufgelistet. Optionale Werte sind in eckige Klammern gesetzt ([-t vfstype]), alle anderen sind also obligatorisch (device dir).

Die (meist einbuchstabigen) Optionen werden als Kette aufgelistet (-fnrsvw). Das bedeutet, als daß eine beliebige Kombination dieser Optionen möglich ist (ob sie sinnvoll ist, wird hier nicht berücksichtigt).

Die Reihenfolge von Parameter sind obligatorisch, alternative Möglichkeiten werden durch ein Pipesymbol getrennt aufgesitet (device|dir).

5.1.3 Befehlsname

  • Befehlsnamen bestehen aus einer Zeichenkette die den Einsatzzweck beschreiben.
  • Sie dürfen keine Whitespaces enthalten und bestehen meist aus Kleinbuchstaben.

5.1.4 Optionen

Optionen steuern die Arbeitsweise des Befehls.

Mehrerer Optionen können hinter einem einzigen Bindestrich gesammelt oder einzeln mit einem Bindestrich aufgelistet werden.

ls·-lrt

ls -l -r -t

Bei Optionen, die einen Parameter erfordern kann dieser der Option direkt folgen oder durch Leerzeichen getrennt werden:

tail -n100

tail -n 100

Werden solche Optionen mit anderen kombiniert, müssen sie als letzte auftreten oder mit einem eigenen Bindestrich eingeführt werden.

tail -fn100

tail -f -n100

5.1.5 Dateiname(n)

Kommandos bei den als Parameter [FILE] angegeben wurde kann eine Liste von Dateien mitgegeben werden, auf die das Kommando angewandt werden soll.

ls datei1 datei2

Einige Befehle erwarten die Eingabe über die Standardeingabe wenn keine Datei angegeben wurde.

cat

Oft kann die Dateiliste auch Dateimuster enthalten die auf mehrere Dateien zutreffen.

ls datei*

Bei einigen Befehlen ist die Anzahl der Dateien festgelegt,

diff datei1 datei2

oder die Reihenfolge der übergebenen Dateien wichtig:

mount /dev/sda1 /mnt

5.1.6 Informationen mitgeben

Es gibt zwei Möglichkeiten, einem UNIX-Kommando beim Aufruf Informationen mitzugeben:* Auf der Kommandozeile

  • Über die Standardeingabe

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 Dateiinhalt in die Standardeingabe des Kommandos.

Auch für die Ausgabe gibt es zwei Möglichkeiten: * Die Standardausgabe eines Programms kann durch die Backquotes (` `) oder durch $( ) als Parameter an ein Kommando übergeben werden.

  • Umgekehrt erlaubt das Kommando echo die Ausgabe der Kommandoparameter auf der Standardausgabe oder per Umleitung in eine Datei oder Pipe.

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

5.1.7 VORSICHT - UNIX-Benutzer sind "mündig"!

Wird ein Kommando ausgeführt, fragt der Befehl nicht, ob Sie das 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. So kann die Kommandobearbeitung der Shell verfolgt und studiert werden. Die Kenntnis des dann folgenden Ablaufs erlaubt es, zu verstehen, warum etwas vielleicht nicht so klappt, wie man es sich vorgestellt hat. Daher sollen diese Schritte hier beschrieben werden.

5.2 Ausführung von Kommandos

Wird eine Kommandozeile abgesetzt, werden deren Teile in Wörter (Token) aufgeteilt.

Wörter die der Parser als Variablenzuweisung markiert hat (vor dem Kommandonamen) und Umleitungen werden für die spätere Referenzierung gespeichert.

Wörter die keine Variablen oder Umleitungen sind, werden expandiert; das erste übrigbleibende Wort nach den Expansionen wird als Kommandoname benutzt und der Rest als Argumente dieses Kommandos.

Dann werden die Umleitungen ausgeführt und die Zeichen die Variablen zugewiesen wurden expandiert; ist dies kein Kommandoname, haben die Variablen Auswirkung auf die aktuelle Shell-Umgebung.

Ein wichtiger der Aufgaben der Shell ist die Suche nach Kommandos. Die Bash macht das wie folgt: * Prüfe, ob das Kommando einen Slash enthält. Wenn nicht prüfe, ob die Liste der Funktionen das gesuchte Kommando enthält.

  • Ist das Kommando keine Funktion, suche in der Liste der eingebauten Befehle.
  • Ist das Kommando weder eine Funktion noch ein eingebauter Befehl, suche danach durch Analyse der Verzeicnisse, die in der Variable PATH gelistet sind.Die Bash nutzt einen Hash-Tabelle im Arbeitsspeicher, um Pfadsuchen zu vermieden.
  • Ist die Suche erfolglos wird eine Fehlermeldung und der Exit Status 127 ausgegeben.
  • War die Suche erfolgreich oder falls das Kommando einen Slash enthält, führt die Shell das Kommando in einer separaten Ausführungsumgebung aus.
  • Schlägt die Ausführung fehl weil die Datei nicht ausführbar und kein Verzeichnis ist, wird angenommen es handle sich um ein Shellscript.
  • Wurde das Kommando nicht asynchron gestartet, wartet die Shell auf die Beendigung des Kommandos und sammelt den Exit Status.

5.2.1 Allgemein

Die Bash bestimmt den Typ des Programms das ausgeführt werden soll. Normale Programme sind Systemkommandos die in kompilierter Form im System vorliegen. Wenn solch ein Programm ausgeführt wird, wird ein neuer Prozess erzeugt indem die Bash eine exakte Kopie von sich selbst erstellt.

Dieser Kindprozess hat die gleiche Umgebung wie sein Elternprozess, nur die Prozess-ID ist eine andere. Diesen Vorgang nennt man forking und wird durch einen exec-Systemaufruf durchgeführt.

Der fork-and-exec Mechanismus ersetzt das alte Kommando mit einem neuen, wobei die Umgebung in der das neue Kommando ausgeführt wird erhalten bleibt, einschließlich der konfigurierten Ein- und Ausgabegeräte, Umgebungsvariablen und Prioritäten.

So werden alle UNIX-Prozesse erstellt, so auch bei Linux Betriebssystemen. Selbst der erste Prozess init mit der Prozess ID 1 wird während des Bootvorgangs im sogenannten bootstrapping als fork erstellt.

5.2.2 Shell Skripte

Wenn eine Datei, die Shell-Befehle enthält, als erste non-option Argument der Bash beim Start (ohne -c oder -s) übergeben wird, erzeugt dies eine nicht-interaktive Shell. Diese Shell sucht nach der Skriptdatei im aktuellen Verzeichnis, danach in den Verzeichnissen der PATH-Variable.

5.2.3 Ausführung von Shell-Skripten

Ist das Programm, das ausgeführt wird ein Shell-Skript, erstellt die Bash einen neuen Kind-Prozess mit fork. Diese Subshell liest das Shell-Skripts Zeile für Zeile.

Die Kommandos jeder Zeile werden gelesen, interpretiert und ausgeführt, als würden sie direkt über die Tastatur eingegeben werden. Solange der Subshell jede Zeile des Skripts ausführt, wartet die Eltern-Shell bis Ihr Kind-Prozess beendet ist.

Sind im Shell-Skript keine Zeilen mehr vorhanden, terminiert die Subshell, die Eltern-Shell erwacht und zeigt einen neuen Prompt.

5.2.4 Eingebaute Shell Befehle (built-in)

Eingebaute Shell-Befehle sind Befehle, die die Shell selber enthält. Wird der Name einen einbauten Kommandos als erstes Wort eines einfachen Kommandos benutzt führt die Shell dieses Kommando direkt aus ohne einen neuen Prozess zu erstellen.

Eingebaute Befehle sind notwendig, um Funktionen zu implementieren, die nicht oder nur umständlich mit externen Werkzeugen umgesetzt werden können.

5.2.4.1 Typen eingebauter Befehle
5.2.4.1.1 Bourne Shell built-ins

:, ., break, cd, continue, eval, exec, exit, export, getopts, hash, pwd, readonly, return, set, shift, test, [, times, trap, umask, unset.

5.2.4.1.2 Bash built-ins

alias, bind, builtin, command, declare, echo, enable, help, let, local, logout, printf, read, shopt, type, typeset, ulimit, unalias.

5.2.4.1.3 Special built-ins

Wird die Bash im POSIX Modus ausgeführt, unterscheiden sich die special built-ins von den anderen eingebauten Kommandos in drei Punkten* Special built-ins werden bei der Kommandosuche vor Shell-Funktionen gefunden

  • Eine nicht-interaktive Shell terminiert, wenn ein special built-in einen Fehler zurück gibt
  • Dem Kommando vorausgehende Zuweisungen bleiben nach Beendigung des Kommandos in der Shell-Umgebung gültig

POSIX special built-ins sind* , ., break, continue, eval, exec, exit, export, readonly, return, set, shift, trap, unset

5.2.5 Anwendungen in anderer Sprache starten

Nutzt man bspw. als Systemsprache Englisch und will ab und zu im Terminal ein Programm in Deutsch starten, dann geht dies folgendermaßen (hier am Beispiel von inkscape):

LANGUAGE=de inkscape

Statt de kann man auch jede andere Sprache nehmen, z.B. fr für Französisch. Voraussetzung sind natürlich die jeweils separat zu installierenden Sprachpakete (Lokalisierung).

5.3 Kommandozeile editieren

Wer sich effizient auf der Kommandozeile bewegen kann, hat viele Aufgaben in Windeseile erlegt.

Unter Linux wird als Standart-Shell die bash eingesetzt. Diese bietet einige praktische Funktionen und Besonderheiten gegenüber den Gewohnheiten von Windowsbenutzern.

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.

5.3.1 Editierfunktionen im Emacs-Modus

[Esc], [F] springt bis zum nächsten Wortende
[Esc], [B] springt bis zum vorigen Wortanfang
[Strg]+[E] springt zum Zeilenende
[Strg]+[A] springt zum Zeilenanfang
[ALT]+[D] Wort löschen
[STRG]+[T] Die beiden vorangegangenen Zeichen vertauschen
[ALT]+[T] Die beiden vorangegangenen Wörter vertauschen

Abhängig von der eingesetzten Terminalemulation können Sie Kombinationen mit der [Esc]-Taste oft auch mit der [Alt]-Taste nachbilden.

Statt also nacheinander [Esc] und [F] zu drücken, funktioniert meist auch die Kombination [Alt]+[F].

Die Fülle der Navigationskommandos der Bash, um sich dem Fehler in der Eingabe anzunähern, sind so mannigfaltig, das wohl die wenigsten Anwender ohne nachzuschlagen, alle aufzählen könnten.

Aber zum Glück genügt die Kenntnis einer kleinen Teilmenge, um den Cursor gekonnt in Position zu bringen.

Bei entsprechender Konfiguration helfen die Nummern- und Navigationstasten der Tastatur schnell zum Ziel, jedoch sind diese in vielen Terminalemulationen nicht vorhanden, so dass Sie auf die »normalen« Navigationsmechanismen der Bash angewiesen sind.

5.3.2 Kommandozeilen vervollständigen

Eine der größten Vorzüge der Bash gegenüber anderen Shells sind die umfangreichen Möglichkeiten, unvollständige Eingaben automatisch zu ergänzen.

Die Bash ist somit in der Lage, sowohl Dateinamen, Kommandonamen, Benutzernamen, Rechnernamen als auch Variablennamen zu ergänzen, sofern die bisher eingegebenen Zeichen eine eindeutige Zuordnung zulassen.

Eine Variablennamensergänzung wird versucht, wenn das erste Zeichen ein $ ist; einem Benutzernamen muss die Tilde ~ und dem Rechnernamen ein @ voran stehen, um eine solche Ergänzung zu erzwingen.

In allen anderen Fällen wird zunächst eine Kommando- und anschließend eine Dateinamensergänzung versucht.

Beispiel

Um den Inhalt der Datei /usr/share/doc/packages/cron/README zu betrachten, geben Sie folgende Zeile ein:

less /usr/share/doc/packages/cron/README

Sie mussten 35 mal die Tastatur bemühen... Versuchen Sie nun die folgende Tastenfolge:

less /u[Tab]/s[Tab]/d[Tab]/p[Tab]/c[Tab]/R[Tab]

Mit etwas Glück steht nun - nach nur 6 Tastaturanschlägen - dieselbe vollständige Kommandozeile zur Verfügung. Falls nicht, hat die Bash sicherlich mit einem Signal reagiert?

Die Bash ergänzt Ihre Eingabe nur, wenn beim Drücken der [Tab]-Taste die bislang eingegebene Zeichenkette eindeutig expandiert werden kann. Auf meinem System ist das bei obigem Beispiel der Fall, bei dem Ihren womöglich nicht.

Reagiert die Bash mit einem Signal, so erreichen Sie durch ein zweites Betätigen von [Tab] die Anzeige einer Liste aller Ergänzungen:

y[Tab] # die Bash reagiert mit einem Piepton

y[Tab][Tab]
yacc       ypcat       ypdomainname  ypwhich     yuvtoppm
ybmtopbm   ypchfn      ypmatch       yuvsplittoppm
yes        ypchsh      yppasswd      yuvsum 

Kann die Zuordnung nicht vollzogen werden, müssen Sie weitere Zeichen eingeben und anschließend die [Tab]-Taste erneut bemühen:

yb[Tab]

ybmtopbm

finger @l[Tab]
finger @localhost

ls ~r[Tab]
ls ~root/

Die Tabulatortaste ist somit die nützlichste Eingabehilfe der Bash. Aber sie ist nicht die Einzige:

[Alt]-[?] Zeigt alle Möglichkeiten der Ergänzung an (wie [Tab][Tab])
[Alt]-[*] Fügt eine Liste aller möglichen Ergänzungen ein
[Alt]-[/] Versucht eine Dateinamenergänzung
[Alt]-[$] Versucht eine Variablenergänzung
[Alt]-[@] Versucht eine Rechnernamenergänzung
[Alt]-[~] Versucht eine Benutzernamenergänzung
[Alt]-[!] Versucht eine Kommandonamenergänzung (Alias, Funktion, eingebautes Kommando, Programm)

Beispiel

Die Plattenplatzbelegung aller Benutzer soll überprüft werden.

Benutzer, die weniger als 1k Speicherkapazität verwenden (0 Blöcke), werden ausgenommen:

du -s ~[Alt][*] 2>/dev/null | grep -v ^0

du -s ~adabas ~amanda ~at ~bin ~codadmin ~cyrus ~daemon ~db2as ~db2fenc1 ~db2inst1  ~dbmaker ~dpbox ~fax ~fib ~firewall ~fixadm ~fixlohn ~fnet ~ftp ~games ~gdm ~gnats             ~informix ~ingres ~irc ~ixess ~lnx ~lp ~man ~mdom  ~mysql ~named ~news ~nobody ~nps             ~oracle ~postfix ~postgres ~root ~skyrix ~squid ~thomas ~user1 ~user2 ~user3 ~uucp             ~virtuoso ~wwwrun ~yard ~zope 2>/dev/null | grep -v ^0

5270    /bin
11080   /sbin
39      /var/spool/lpd
1584    /var/cache/man
392240  /home/user

5.3.3 Positionieren des Cursors

[Strg]+[a] bzw. [Strg]+[e] Bewegt den Cursor an den Beginn bzw. das Ende der Zeile
[Strg]+[f] bzw. [Strg]+[b] Bewegt den Cursor ein Zeichen vor bzw. zurück
[Alt]+[f] bzw. [Alt]+[b] Bewegt den Cursor ein Wort vor bzw. zurück

5.3.4 Text Löschen und einfügen

[Strg]+[d]

Löscht das Zeichen unter dem Cursor

[Strg]+[k]

Löscht den Text ab der Cursorposition bis zum Zeilenende

[Strg]+[u]

Löscht den Text links des Cursors bis zum Zeilenanfang

[Alt]+[d]

Löscht ab dem Cursor bis zum Ende des Wortes

[Strg]+[y]

Fügt den zuletzt gelöschten Text an der Cursorposition ein

[Alt]+[y]

Dieses Kommando kann nur unmittelbar nach [Strg]+[y] gerufen werden und rotiert den (Ring)Puffer der gelöschten Texte, dazu ein Beispiel:

Das Beispiel demonstriert das Löschen und Einfügen von Text_

# Cursor an den Anfang des Wortes "Einfügen"
[Alt]+[b],[Alt]+[b],[Alt]+[b]

echo Das Beispiel demonstriert das Löschen und Einfügen von Text

# Löschen von "Einfügen"
[Alt]+[d]

echo Das Beispiel demonstriert das Löschen von Text

# Cursor unter "Löschen" und löschen:
[Alt]+[b],[Alt]+[b],[Alt]+[d]

echo Das Beispiel demonstriert das und von Text

# Einfügen von "Einfügen" aus dem Ringpuffer:
[Strg]+[y],[Alt]+[y]

echo Das Beispiel demonstriert das Einfügen und von Text

# Einfügen von "Löschen" aus dem Ringpuffer hinter "und":
[Alt]+[f],[Strg]+[f],[Strg]+[y],[Alt]+[y]
echo Das Beispiel demonstriert das Einfügen und Löschen  von Text

Der letzte Einfügevorgang funktioniert mit der angegebenen Tastenfolge nur, wenn einzig »Löschen« und »Einfügen« im Ringpuffer enthalten sind.

[Alt]+[u]

Wandelt das Wort ab Cursorposition in Großbuchstaben

[Alt]+[l]

Wandelt das Wort ab Cursorposition in Kleinbuchstaben

[Alt]+[r]

Macht alle Änderungen in der Kommandozeile rückgängig

[Alt]+[c]

Wandelt den Buchstaben an der Cursorposition in einen Großbuchstaben um und springt ans Ende des aktuellen Wortes

Die oben aufgeführten Eingabehilfen beschreiben einzig eine Teilmenge der Möglichkeiten der Bash. Eine vollständige Auflistung liefert sie das eingebaute Kommando

bind -P.

Ausgabe anhalten

[Strg]+[s]

Ausgabe fortsetzen

[Strg]+[q]

Bildschirm wiederherstellen

[Strg]+[l]

Ctrl-S und Ctrl-Q waren früher bei langsamen, seriellen Terminals noch als Reaktionstest brauchbar. Heute sind sie nicht mehr zum Steuern der Ausgabe verwendbar, weil die Bildschirmanzeige zu schnell durchläuft.

Trotzdem kann Ctrl-S Ärger machen, wenn Sie versehentlich auf die Taste kommen. Dann bleibt die Ausgabe stehen und man hat das Gefühl, der Rechner reagiert nicht mehr. Also erst mal versuchsweise Ctrl-Q drücken.

5.3.5 Bereits erwähnte und Auswahl weiterer Tastenkürzel

Strg + Alt + T Standard zum Öffnen der meisten Terminalprogramme. Muss bei KDE erst aktiviert werden.
Strg + Alt + F1 bis F6 Öffnen der Virtuellen Konsolen 1 bis 6.
Strg + Alt + F7 Von der Virtuellen Konsole zurück zur grafischen Oberfläche.
Bearbeiten
Tasten Aktion
Strg + A Cursor an den Anfang der Eingabezeile bewegen.
Strg + E Cursor an das Ende der Eingabezeile bewegen.
Alt + . Einfügen der letzten Option (Wort) der letzten Eingabe – mehrmaliges Betätigen möglich.
Strg + H Löscht letztes Zeichen vor Cursorposition, analog zu ⌫ .
Strg + D Löscht nächstes Zeichen ab Cursorposition, analog zu Entf , auf einer leeren Zeile entspricht dies der Eingabe von exit.
Strg + W Löscht letzten Begriff vor der Cursorposition.
Strg + U Löscht alles ab der Cursorposition bis Anfang der Eingabezeile.
Strg + K Löscht alles ab der Cursorposition bis Ende der Eingabezeile.
Strg + ⇧ + X Löschen des Bildschirms vor aktueller Zeile.
Strg + ⇧ + C Kopieren des markierten Texts in die Zwischenablage.
Strg + ⇧ + V Einfügen der Zwischenablage an der Cursorposition.
Strg + C Bricht laufenden Befehl/Prozess ab.
Suchen und Blättern
Tasten Aktion
Rückwärtsblättern in den letzten Befehlseingaben.
Vorwärtsblättern in den letzten Befehlseingaben.
Tab ⇆ Autovervollständigung von Befehlen und Dateinamen.
Strg + R Suchen in den letzten Eingaben ( ← bricht Suche ab, ⏎ führt Kommando erneut aus) – mehrmaliges Betätigen möglich.
Strg + ⇧ + F Suchen in der Bildschirmanzeige.
Strg + ⇧ + ↑ Zeilenweises Rückwärtsblättern der Bildschirmanzeige.
Strg + ⇧ + ↓ Zeilenweises Vorwärtsblättern der Bildschirmanzeige.
⇧ + Bild ↑ Seitenweises Rückwärtsblättern der Bildschirmanzeige.
⇧ + Bild ↓ Seitenweises Vorwärtsblättern der Bildschirmanzeige.

5.4 History

Dies vereinfacht die Bedienung in der Praxis enorm.

Als Standardwert werden die letzten 500 Befehle gespeichert. Einstellen lässt sich dieser Wert mit einem Editor in der Datei ~/.bashrc. Mit dem Eintrag HISTSIZE=2000 wird die History z.B. auf 2000 Befehle erweitert.

The Bash shell provides command-line tools for editing and manipulating a user's command history. This is primarily a convenience, a means of saving keystrokes.

Bash history commands: # history

  1. fc

history

   1  mount /mnt/cdrom
   2  cd /mnt/cdrom
   3  ls
    ...

Internal variables associated with Bash history commands: * $HISTCMD

  • $HISTCONTROL
  • $HISTIGNORE
  • $HISTFILE
  • $HISTFILESIZE
  • $HISTSIZE
  • $HISTTIMEFORMAT (Bash > 3.0)
  • !!
  • !$
  • !#
  • !N
  • !-N
  • !STRING
  • !?STRING?
  • ^STRING^string^

5.4.1 Befehlsspeicher (History)

Datei:Grafik5.pngDie Bash merkt sich alle eingegebenen Befehle in einer Datei, der Kommandozeilen-History. Diese Datei befindet sich im Home-Directory des Benutzers und heißt .bash_history.

Der Kommandozeilenspeicher - kurz History - ist eine Liste, die die zuvor eingegebenen Kommandozeilen enthält. In der Bash ist es möglich, die zuletzt eingegebenen Befehle mit den Pfeiltasten ( ↑ / ↓ ) durchzublättern oder zu durchsuchen.

Mit den Cursortasten auf/ab kann man in dieser Liste blättern. Darüber hinaus stehen folgende History-Befehle zur Verfügung: # [Strg]+[R] sucht nach dem letzten Kommando, das die eingegebenen Zeichen enthält

  1. Wiederholte Eingabe von [Strg]+[R] such den nächsten Treffer

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

Um wie viele Einträge es sich maximal handelt, sagt die Shellvariable HISTSIZE; ist die Kapazität erschöpft, werden die ältesten Einträge gelöscht.

Die Erreichbarkeit und Eigenschaften der History werden weitest gehend über Bash-interne Variablen (siehe set und shopt) gesteuert.

5.4.2 Setting History Defaults

Before we begin actually using the history, let's adjust some bash settings to make it more useful.

Bash allows you to adjust the number of previous commands that it stores in history. It actually has two separate options for this: the HISTFILESIZE parameter configures how many commands are kept in the history file, while the HISTSIZE controls the number stored in memory for the current session.

This means you can set a reasonable cap for the size of history in memory for the current session, and have an even larger history saved to disk that you can examine at a later time. By default, bash sets very conservative values for these options, so we'll expand them to take advantage of a larger history. Some distributions already increase the default bash history settings with slightly more generous values.

Open your ~/.bashrc file with an editor to change these settings:

nano ~/.bashrc

Search for both the HISTSIZE and HISTFILESIZE parameters. If they are set, feel free to modify the values. If these parameters aren't in your file, add them now. For our purposes, we can easily get away with saving 10000 lines to disk and loading the last 5000 lines into memory. This is a conservative estimate for most systems, but adjust it down if you see a performance impact:

HISTSIZE=5000 HISTFILESIZE=10000

By default, bash writes its history at the end of each session, overwriting the existing file with an updated version. This means that if you are logged in with multiple bash sessions, only the last one to exit will have its history saved.

We can work around this by setting the histappend setting, which will append instead of overwrite the history. This may be set already, but if it is not, you can enable this by adding this line:

shopt -s histappend

If we want to have bash immediately add commands to our history instead of waiting for the end of each session (to enable commands in one terminal to be instantly be available in another), we can also set or append the history -a command to the PROMPT_COMMAND parameter, which contains commands that are executed before each new command prompt.

To do this correctly, we need to do a bit of a hack. We need to append to the history file immediately with history -a, clear the current history in our session with history -c, and then read the history file that we've appended to, back into our session history with history -r.

You can do this like so:

export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"

When you are finished, save the file and exit.

To implement your changes, either log out and back in again, or source the file by typing:

source ~/.bashrc

5.4.2.1 History aktivieren

Um die nachfolgenden Ausführungen nachvollziehen zu können, sollten folgende Shellvariablen aktiv sein:

Ist die History aktiv?

set -o | grep history

history         on

Ist die History-Substitution aktiv?

set -o | grep histexpand

histexpand      on

Sicher haben Sie schon bemerkt, dass eine fehlerhafte Kommandozeile Ihnen nicht nochmals zum Editieren zur Verfügung steht.

Indem Sie die Variable histreedit setzen (shopt -s histreedit) steht Ihnen die bemängelte Kommandozeile sofort zum Bearbeiten zur Verfügung.

5.4.2.2 History-Variablen

Neben den eingangs beschriebenen Variablen (die allesamt mit set oder shopt zu (de)aktivieren sind) wird der Kommandozeilenspeicher durch weitere Variablen beeinflusst:

5.4.2.2.1 HISTCMD

Der Index des aktuellen bearbeiteten Kommandos in der History-Liste steht hier. Diese Variable wird nur intern benutzt, um mit den später beschriebenen Kommandos in der History navigieren zu können.

5.4.2.2.2 HISTCONTROL

Über diese Variable lässt sich in gewissen Grenzen steuern, welche Eingabezeilen als Kandidaten für die Aufnahme in die Historyliste in Frage kommen. * Ist die Variable nicht gesetzt, oder steht irgend etwas außer "ignorespace", "ignoredups" oder " ignoreboth" drin, werden alle korrekten Eingabezeilen aufgenommen.

  • ignorespace schließt Eingaben aus, die mit einem Leerzeichen, Tabulator oder Zeilenumbruch beginnen.
  • ignoredups verhindert die Aufnahme von Eingaben, die genau so in der unmittelbar vorangegangenen Kommandozeile bereits erschienen.
  • ignoreboth schließlich kombiniert "ignorespace" und "ignoredups".
5.4.2.2.3 HISTSIZE

Schließlich beinhaltet diese Variable die Anzahl maximal möglicher Einträge in der History.

Diese Anzahl kann durchaus verschieden vom Wert in HISTFILESIZE sein, da letzteres nur bei Beendigung (oder expliziter Aufforderung) geschrieben wird.

5.4.2.2.4 HISTIGNORE

Diese Liste kann eine Doppelpunkt-separierte Liste von Mustern enthalten. Eine Kommandozeile, die diesem Muster entspricht, wird von der Aufnahme in die History-Liste ausgeschlossen. Das Muster kann Metazeichen enthalten.

Zusätzlich kann & als Platzhalter für den vorherigen Eintrag in der History-Liste verwendet werden.

Um bspw. alle Zeilen, die mit "echo" beginnen oder "ls" enthalten dem Kommandozeilenspeicher vorzuenthalten, muss die Variable wie folgt belegt sein:

export HISTIGNORE="echo*:*ls*"

5.4.2.2.5 HISTFILE

Diese Variable enthält den vollständigen Namen der Datei, in der die History zu speichern ist. Sie sollte immer gesetzt sein.

5.4.2.2.6 HISTCHARS

Die weiter unten beschriebene History-Substitution wird vorgenommen, wenn die Eingabe mit einem bestimmten Zeichen beginnt (! oder ^).

Mit dieser Variable können die voreingestellten Zeichen überschrieben werden.

Soll das Ausrufezeichen (!) ausgetauscht werden, genügt die Belegung von histchars mit dem neuen Zeichen.

Soll das Dach (^) verändert werden, muss das Ausrufezeichen (oder das Zeichen, das anstelle dessen verwendet wird) vorangestellt werden (also "!Zeichen").

5.4.2.2.7 HISTFILESIZE

Wie viele Zeilen maximal die unter HISTFILE benannte Datei enthalten darf, wird hiermit fest gelegt. 500 ist ein typischer Wert.

5.4.3 History-Zugriff

Die einfachste Methode des Zugriffs ist die Verwendung vordefinierter Tastenkombinationen:

[Strg]+[P] bzw. [Strg]+[N]

vorherige/nächste Element der History-Liste

[Strg]+[R]

Suche rückwärts nach einer Zeile, die mit dem eingegeben Muster übereinstimmt:

[Strg]+[R]

# Nach Eingabe von "fi"
(reverse-i-search)`fi': chown :fibel bla
# Nach Eingabe von "fin"
(reverse-i-search)`fin': find /tmp -user user
# Nach Eingabe von "fin[Strg]+[R]"
(reverse-i-search)`fin': find /root -name "*txt" 2>1

Durch wiederholte Eingabe von [Strg]+[R] wird die nächste Zeile geliefert, auf die das Muster zutrifft.

[Strg]+[S]

Suche vorwärts; Anwendung analog zu [Strg]+[R].

[Alt]+[<] bzw. [Alt]+[>]

Erstes bzw. letztes Element der History-Liste (der Zugriff auf das letzte Element ist selten konfiguriert)

5.4.3.1 Reviewing your Previous Bash History

The way that we review bash history is to use the history command. This will print out our recent commands, one command per line. This should output, at most, the number of lines you selected for the HISTSIZE variable. It will probably be fewer at this point:

history

  . . .
  44  man fc
  45  man bash
  46  fc -l -10
  47  history
  48  ls -a
  49  vim .bash_history 
  50  history
  51  man history
  52  history 10
  53  history

It also prints the history number for each command. Each command is associated with a number for easy reference. You will see why this is useful in a moment.

We can truncate the output by specifying a number after the command. For instance, if we want to only see the last 5 commands typed, we can type:

history 5

  50  history
  51  man history
  52  history 10
  53  history
  54  history 5

To find all of the history commands that involve a certain string, an easy way of getting an overview is to simply pipe it to grep. For example, we can search for the lines that have cd by typing:

history | grep cd

  33  cd Pictures/
  37  cd ..
  39  cd Desktop/
  61  cd /usr/bin/
  68  cd
  83  cd /etc/
  86  cd resolvconf/
  90  cd resolv.conf.d/
5.4.3.2 Scrolling through Bash History

There are a few ways that we can scroll through our bash history, putting each successive command on the command line to edit.

The most common way of doing this is to press the up arrow key at the command prompt.

Each additional press of the up arrow key will take you further back in your command line history. If you need to go the other direction, the down arrow key traverses the history in the opposite direction, finally bringing you back to your current prompt.

If moving your hand all the way over to the arrow keys seems like a big hassle, you can move backwards in your command history using the CTRL-p combination, and use the CTRL-n combination to move forward in history again.

If you want to jump back to the current command prompt, you can do so by typing Meta->. In most cases, the "meta" key and the ">" will mean typing ALT-Shift-.. This is useful if you find yourself far back in your history and want to get back to your current command.

You can go to the first line of your command history by doing the opposite maneuver and typing Meta-<. This typically means pressing ALT-Shift-,.

To summarize, these are some keys to scroll through the history and jump to either end:* UP arrow key: Scroll backwards in history

  • CTRL-p: Scroll backwards in history
  • DOWN arrow key: Scroll forwards in history
  • CTRL-n: Scroll forwards in history
  • ALT-Shift-.: Jump to the end of the history (most recent)
  • ALT-Shift-,: Jump to the beginning of the history (most distant)
5.4.3.3 Executing Commands from your Bash History

Printing off our history is nice, but, by itself, it doesn't really help us access those commands easily, except as a reference. However, we can quickly recall any of the returned output using a special syntax.

We can recall any of our previous history by its number preceded by an exclamation point (!). For instance, if your history looks like mine above, you could see the man page for the history command quickly by typing:

!51

This will immediately recall and execute the command associated with the history number 51.

We can also execute commands relative to our current position. We can do this by using the !-n syntax, where "n" is replaced by the number of commands ago we want to recall.

For instance, if we want to recall and execute a command that we typed before our most recent one, we could type !-2. So if we listed the contents of a long directory path, echoed something and wanted to list again, our session might look like this:

ls /usr/share/doc/manpages echo hello !-2 # lists the contents again

To re-execute the previous command, bash provides a shortcut that we can use instead of !-1. The shortcut is !!, which will substitute the most recent command and execute:

!!

Many people use this when they type a command that they realize they needed sudo privileges to execute. Typing sudo !! will re-execute the command with sudo in front of it. The session might look like this:

touch /etc/hello touch: cannot touch `/etc/hello': Permission denied sudo !! sudo touch /etc/hello [sudo] password for demouser:

This demonstrates another property of this syntax. They are pure substitutions, and can be incorporated within other commands at will.

5.4.3.4 Korrektur des letzten Befehls

Bei Tippfehlern kann die letzte Kommandozeile leicht korrigiert werden:

moore /irgendein/pfad/zur/datei.txt

Die Zeile kann korrigiert und erneut ausgeführt werden:

^moore^more

Das zuvor falsch geschriebenen moore wird durch more ersetzt und der Pfad wieder angehängt. So kann auch der vorhergehende Befehl durch einem neuen ersetzten werden.

5.4.3.5 Letzten Befehl als root ausführen

Viele Eingaben auf der Konsole können nur als Superuser ausgeführt werden, wofür als normaler Nutzer mangels entsprechender Berechtigung der Befehl ‘sudo’ vor die Eingabe gesetzt wird.

Dies fällt häufig erst nach dem Absenden des Befehls auf. So kann der letzte Befehl mit sudo wiederholt werden:

sudo !!

5.4.4 Durchsuchen

Die Befehlsliste lässt sich mit der Tastenkombination Strg + R durchsuchen. Durch Eingabe von

history

lässt sich auch eine Liste der eingegebenen Befehle ausgeben, die man mit Hilfe von egrep filtern kann:

history | egrep webserver

gibt zum Beispiel alle Befehle an, die die Zeichenfolge "webserver" enthalten haben.

Indem man einer Zeichenfolge ein Ausrufezeichen voranstellt, sucht die Bash in der History nach dem letzten Eintrag, der mit den selben Zeichen beginnt und führt diesen Befehl aus.

5.4.4.1 Searching through Bash History

Although piping the history command through grep is definitely the easiest way of accomplishing some procedures, it isn't ideal in many situations.

Bash includes search functionality for its history. The typical way of utilizing this is through searching backwards in history (most recent results returned first) using the CTRL-r key combination.

For instance, you can type CTRL-r, and begin typing part of the previous command. You only have to type out part of the command. If it matches an unwanted command instead, you can press CTRL-r again to see the next result.

If you accidentally pass the command you wanted, you can move in the opposite direction by typing CTRL-s. This also can be useful if you've moved to a different point in your history using the keys in the last section and wish to search forward.

Note: In many terminals, the CTRL-s is actually mapped to suspend the terminal session. This will intercept any attempts to pass CTRL-s to bash, and will "freeze" your terminal. To unfreeze, simply type CTRL-q to unsuspend the session.

This suspend and resume feature is not needed in most modern terminals, and we can turn it off without any problem by typing:

stty -ixon

We should add this to our ~/.bashrc file to make this change permanent as well.

If you try again, it should work as expected to allow you to search forwards.

5.4.4.1.1 Searching after You've Typed Part of the Command

A common scenario to find yourself in is to type in part of your command, and then realize that you have executed it previously and can search the history for it.

The correct way of searching using what is already on your command line is to move your cursor to the beginning of the line with CTRL-a, call the reverse history with CTRL-r, paste the current line into the search with CTRL-y, and then using the CTRL-r again to search in reverse.

For instance, suppose we want to update our package cache on an Ubuntu system. We've already typed this out recently, but we didn't think about that until after we've typed the sudo in again:

sudo

At this point, we realize that this is an operation we've definitely done in the past day or so. We can hit:

CTRL-a

This moves our cursor to the beginning of the line.

CTRL-r

We call our reverse incremental history search. This has a side effect of copying all of the content on the command line that was after our cursor position. It puts this into a clipboard.

CTRL-y

We paste the command segments that we'd just copied from the command line into the search.

CTRL-r

We move backwards in our history, searching for commands containing the content we've just pasted.

This might seem like a huge pain in the neck, but it's actually not too bad when you get used to it. It is extremely helpful when you find yourself in that awkward position where you've typed out half of a complex command and know you're going to need the history to finish the rest.

To make it easier, you can think of this as a simpler, compound command:

CTRL-aryr

5.4.5 Gezieltes Blättern aktivieren

Um die History der Bash gezielt zu durchblättern, lohnt es sich, folgende Zeilen in der Datei /etc/inputrc einzufügen [1]:

"\e[5~": history-search-backward "\e[6~": history-search-forward

Möchte man diese Funktion nur für einen bestimmten User aktivieren und nicht systemweit, so kopiert man die Datei /etc/inputrc nach /home/$USER/.inputrc, und nimmt die Änderung dort vor.

Durch Drücken der Tasten Bild ↑ und Bild ↓ kann man die History der Bash anschließend nach Einträgen durchsuchen, welche mit den Worten beginnen, die vor der aktuellen Cursorposition stehen. Beispiel:

sudo vi⌷ /etc # ⌷ = Cursor

Drückt man jetzt Bild ↑ oder Bild ↓ , wird die History nach Einträgen durchblättert, welche mit sudo vi beginnen.

5.4.5.1 Hinweis

Wird die /home/$USER/.inputrc verwendet, muss darin in der ersten Zeile

$include /etc/inputrc

stehen, damit die Einstellungen darin ebenfalls beachtet werden.

5.4.6 Sofort speichern

Man kann die History auch sofort nach der Ausführung jeden Befehls speichern lassen. Das hilft beispielsweise zum einen bei dem Problem unter einigen Versionen wie Ubuntu 14.04 und anderen Varianten, dass die History nicht gespeichert wird, außer wenn man exit eingibt. Zum anderen kann man dadurch die komplette History direkt in neu geöffnete Terminal-Tabs übernehmen lassen, obwohl die anderen Tabs noch nicht geschlossen sind. Dazu muss man in der Datei ~/.bashrc folgende Zeile eintragen:

PROMPT_COMMAND="history -a"

Möchte man erreichen, dass sogar bei parallel geöffneten Tabs bzw. Fenstern die History aus allen Tabs nach jedem Befehl gespeichert und dadurch jedem bereits geöffneten anderen Tab sofort zur Verfügung gestellt wird, ohne dafür einen neuen Tab öffnen zu müssen, trägt man stattdessen folgende Zeile ein:

PROMPT_COMMAND="history -a; history -c; history -r"

5.4.6.1 Hinweis

Dabei wird die temporäre History-Liste erst in die History-Datei angehangen und gespeichert, anschließend die History-Liste gelöscht und anschließend die dann komplette History aus der History-Datei neu in die History-Liste eingelesen, um sofort die vollständige History aus der Datei in der durchscrollbaren Liste zur Verfügung zu haben.

Diese Lösung klingt zwar zunächst am vorteilhaftesten für viele Zwecke, jedoch ist so die Wiederholung des letzten Befehls aus dem selben Tab durch die Pfeiltasten dann nicht mehr direkt möglich, da sich dann Befehle aus den anderen Tabs vordrängeln können, so dass man die Pfeiltaste nach oben dann ggf. mehrfach betätigen muss.

5.4.6.2 Achtung!

Wenn dies beispielsweise ein Löschbefehl für das aktuelle Verzeichnis war, kann sich dies bei unbedachtem Bestätigen des falschen Befehls sehr ungünstig auswirken.

Dadurch hat man allerdings sofort alle Befehle, Optionen und Parameter in allen Tabs zur Verfügung, ohne sie in neue Tabs kopieren zu müssen, wenn man sie dort für weitere Vorgänge braucht. Dazu reicht es aus, in dem Tab, wo man die komplette History aller Tabs sofort benötigt, einmal kurz ⏎ zu betätigen, damit die History von allen Tabs dort neu eingelesen wird.

5.4.7 Bestimmte Befehle ausschließen

Will man nur bestimmte Befehle nicht in der History speichern, so kann man mit der Variablen HISTIGNORE eine schwarze Liste erstellen. Hierzu trägt man beispielsweise

HISTIGNORE="truecrypt -P*:sudo*"

in die Datei ~/.bashrc ein [1]. Dadurch werden alle Befehle, die mit truecrypt -P oder mit sudo beginnen, von der History ausgeschlossen.

5.4.8 Zeitstempel hinzufügen

Möchte man den Zeitpunkt der Eingabe von Befehlen in der History speichern, so kann dies mit der Variablen HISTTIMEFORMAT erfolgen. Hierzu trägt man

HISTTIMEFORMAT="%F %T "

in die Datei ~/.bashrc ein. Allerdings sollte beachtet werden, dass Zeitstempel nicht rückwirkend gesetzt werden können, wodurch diese bei bereits in der Historie befindlichen Einträgen nicht zutreffend sind. Beispielausgabe:

400 2013-01-10 08:00:01 ls 401 2013-01-10 08:00:17 top 402 2013-01-10 08:01:04 ps aux

5.4.9 Am häufigsten verwendete Befehle

Wer interessehalber ein Liste der am häufigsten verwendeten Befehle benötigt, benutzt den folgenden Befehl:

history | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n10

5.4.10 Löschen/deaktivieren

Mit dem Befehl

history -c

löscht man die komplette History der aktiven Shell. Möchte man dazu noch die History vorheriger Sitzungen aus der Datei ~/.bash_history löschen, kann man diese Datei mit den Befehlen

history -c history -w

mit der leeren History der aktiven Shell überschreiben. Einzelne Zeilen lassen sich löschen mit

history -d NNN

NNN repräsentiert hierbei einen dreistelligen Zahlenwert der entsprechenden Zeile.

5.4.10.1 Clear Bash-History
5.4.10.1.1 Completely

The file ~/.bash_history holds the history. To clear the bash history completely on the server, open terminal and type

cat /dev/null > ~/.bash_history

5.4.10.1.2 Permanently

Other alternate way is to link ~/.bash_history to /dev/null

5.4.10.1.3 However

One annoying side-effect is that the history entries has a copy in the memory and it will flush back to the file when you log out.

To workaround this, use the following command (worked for me):

cat /dev/null > ~/.bash_history && history -c && exit

5.4.10.2 Deaktivierung

Es kann unter bestimmten Umständen sinnvoll sein, die History der Bash zumindest für root zu deaktivieren. Dazu muss man in der Datei ~/.bashrc folgende Zeile eintragen:

unset HISTFILE

und die Datei ~/.bash_history löschen.

5.4.11 Advanced History Expansion

We've already touched on some of the most basic history expansion techniques that bash provides. Some of the ones we've covered so far are:* !!: Expand to the last command

  • !n: Expand to command with history number "n".
  • !-n: Expand to command that was "n" number of commands before the current command in history.
5.4.11.1 Event Designators

The above three examples are instances of event designators. These generally are ways of recalling previous history commands using certain criteria. They are the selection portion of our available operations.

We can execute the last "ssh" command by typing something like:

!ssh

This searches for lines beginning with "ssh". If we want to search for a string that doesn't happen at the beginning of the command, we can surround it with "?" characters. For instance, to repeat our least apt-cache search command, we could probably get away with typing:

!?search?

Another trick that you can try is a variation on the !! last history command. You can do a quick search and replace by typing:

^original^replacement^

This will recall the previous command (just like "!!"), search for an instance of "original" within the command string, and replace it with "replacement". It will then execute the command.

This is useful for dealing with things like misspellings. For instance:

cat /etc/hosst cat: /etc/hosst: No such file or directory ^hosst^hosts^

5.4.11.2 Word Designators

After event designators, we can add a colon (:) and add on a word desginator to select a portion of the matched command.

It does this by dividing the command into "words", which are defined as any chunk separated by whitespace. This allows us some interesting opportunities to interact with our command parameters.

The word numbering starts at the initial command as "0", the first argument as "1", and continues on from there.

For instance, we could list the contents of a directory, and then decide we want to change to it, like this:

ls /usr/share/doc/manpages cd !!:1

In cases where we are operating on the last command, we can actually compress this by removing the second "!"and the colon, like this:

cd !1

This will operate in the same way.

We can refer to the first argument as "^" and the final argument as "$" if that makes sense for our purposes. These are more helpful when we use ranges instead of specific numbers. For instance, we have three ways we can get all of the arguments from a previous command into a new command:

!!:1* !!:1-$ !!:*

The lone "" expands to all portions of the command being recalled other than the initial command. Similarly, we can use a number followed by "" to mean that everything after the specified word should be included.

5.4.11.3 Modifiers

The last thing that we can do to augment the behavior of the history line we are recalling is to modify the behavior of the recall to manipulate the text itself. Modifiers are added after an additional colon (:) character at the end of the expansion.

For instance, we can chop off the path leading up to a file by using the "h" modifier (it stands for "head"), which removes the path up until the final slash (/) character. Be aware that this won't work the way you want it to if you are using this to truncate a directory path and the path ends with a trailing slash.

A common use-case for this is if we are modifying a file and realize we'd like to change to the file's directory to do operations on related files.

For instance, we could read the copyright information of a package:

cat /usr/share/doc/manpages/copyright

After being satisfied that we can use the package for our needs, we may want to change to the directory. We can do this by calling the cd command on the argument chain and chopping off the filename at the end:

cd !!:$:h pwd /usr/share/doc/manpages

After we're there, we may want to open that copyright file again to double check, this time in a pager.

We can do the reverse manipulation, chopping off the path and using only the filename with the "t" modifier for "tail". We can search for our last cat operation, and use the "t" flag to pass only the file name:

less !cat:$:t

You could just as easily keep the full absolute path name and this command would work correctly in this instance. However, there are other times where this isn't true. We could be looking at a file nested within a few sub directories below our current directory using a relative path, change to the subdirectory using the "h" modifier, and then we wouldn't be able to rely on the relative path name to reach the file any more.

Another extremely helpful modifier is the "r" modifier, which strips the trailing extension. This could be useful if you are using tar to decompress a file and want to change into the directory afterwards. Assuming the directory produced is the same name as the file, you could do something like:

tar xzvf long-project-name.tgz cd !!:$:r

If your tarball uses the tar.gz extension instead of tgz, you can just pass the modifier twice:

tar xzvf long-project-name.tar.gz cd !!:$:r:r

A similar modifier, "e", removes everything besides the trailing extension.

If you do not want to execute the command that you are recalling, and simply want to find it, you can use the "p" modifier to have bash echo the command instead of executing it.

This is useful if you are unsure of if you're selecting the correct piece. This not only prints it, but also puts it into your history for further editing if you're unhappy with it.

For instance, imagine you ran a find command on your home directory and then realize that you want to run it from the root (/) directory. You could check that you're making the correct substitutions like this (assuming the original command is #119):

find ~ -name "file1" # original command !119:0:p / !119:2*:p find / -name "file1"

If the outputted command we cobbled together is correct, we can execute it easily with:

CTRL-p

This would probably be easier if we could just make substitutions in our command easily. We can do that using the s/original/new/ syntax.

For instance, we could have accomplished that by typing:

!119:s/~/\//

This will substitute the first instance of the search pattern. We can substitute every match by also passing the "g" flag with the "s". For instance if we want to create files named file1, file2, and file3, and then want to create directories called dir1, dir2, dir3, we could do this:

touch file1 file2 file3 mkdir !!:*:gs/file/dir/

5.4.11.4 Conclusion

You should now have a good idea of how you can leverage the history operations available to you. Some of these will probably be more useful than others, but it is good to know that bash has these capabilities in case you find yourself in a position where it would be helpful to dig them up.

If nothing else, the history command alone, the reverse search, and the simple history expansions should help you speed up your work flow.

5.5 Jobkontrolle

fg, bg und jobs sind Befehle für die interne Jobkontrolle der Bash.

Ein laufendes Programm kann mit [Strg] + [Z] in den Hintergrund verschoben werden; dort wird es zunächst angehalten ("suspend").

Das Kommando jobs gibt eine Liste aller derzeit im Hintergrund schlafenden Programme aus.

Dabei wird für jedes Programm die interne Jobnummer angegeben.

Es ist wichtig anzumerken, dass die vergebene Jobnummer in keinem Zusammenhang zu der - etwa von ps - angezeigten Prozessnummer steht.

Mit fg Jobnummer holen Sie diese Task wieder in den Vordergrund.

Soll ein angehaltenes Kommando dagegen im Hintergrund weiterlaufen, verwendet man den Befehl bg Jobnummer.

5.5.1 Prozess im Hintergrund weiterlaufen lassen (bg)

bg [job_spec]

Das Kommando lässt einen zuvor gestoppten Prozess im Hintergrund weiterlaufen. Die Ausführung eines Prozesses wird durch Senden des Signals 19 (SIGSTOP) gestoppt. Befinden sich mehrere Prozesse im Zustand "Stopp", kann der gewünschte Prozess über seine Jobnummer angesprochen werden (siehe jobs).

Beispiel

ls -l > fifo

^z
[1]+  Stopped                 ls --color=tty -l >fifo
bg
[1]+ ls --color=tty -l >fifo &

Die Eingabe von [Strg]+[Z] bewirkt in der Bash das Senden des Signals SIGSTOP an den aktiven Prozess.

5.5.2 Hintergrundprozess in den Vordergrund holen (fg)

fg [job_spec]

Ein im Hintergrund laufender Prozess kann in den Vordergrund geholt werden.

Soll nicht gerade der zuletzt im Hintergrund gestartet Prozess gewählt werden, ist die Angabe der Jobnummer erforderlich, die man mit dem Kommando jobs ermitteln kann.

Beispiel

sleep 100& sleep 500&

[1] 1177
[2] 1178
fg %2 

5.5.3 Anzeige aktiver Jobs (jobs)

jobs [-lnprs] [jobspec ...]

Mit dem Kommando lassen sich alle Hintergrundprozesse ("Jobs") der aktiven Shell anzeigen.

Optionen

-l Zusätzliche Anzeige der PID
-n Nur Prozesse anzeigen, deren Status sich seit dem letzten Aufruf von jobs geändert hat
-p Nur die PIDs anzeigen
-r Nur aktive Prozesse anzeigen
-s Nur gestoppte Prozesse anzeigen

Beispiel

sleep 100&

[1] 1177
jobs
[1]+ Running                 sleep 10000 &
jobs -n
[1] 1177 

5.5.4 Verzögerte Ausgabe (sleep)

sleep Zeit [smhd] ...

sleep wartet, dass die Zeit verstreicht. Voreingestellt sind Sekunden, es können aber auch Minuten, Stunden oder Tage sein.

Diese Funktion wird vor allem in Shellscripts zur vorübergehenden Anzeige von Informationen benutzt.

Optionen

s Sekunden
m Minuten
h Stunden
d Tage

Prozesse, die ihren Status wechseln, quittieren dies mit einer Ausgabe. Wie mit jeder Ausgabe verfahren wird, ist konfigurierbar.

5.5.4.1 Benachrichtigung über Statuswechsel

Vor Anzeige der neuen Eingabeaufforderung

(sleep 1; echo foo)&

[1] 1372
foo
[Enter]

[1]+  Done                    (sleep 1; echo foo)

Durch diese Verzögerung "platzt" der Statusbericht nicht in die Ausgabe des aktuellen Vordergrundprozesses hinein.

Gesteuert wird das Verhalten durch die Shelloption notify (Kommando set). Ist diese Option aktiviert (on), so erreicht uns die Statusmeldung sofort:

set -o notify

(sleep 1; echo foo)&
[1] 1376
foo
[1]+  Done                    (sleep 1; echo foo)

5.6 Verzeichnisstapel

dirs, pushd, popd verwalten den Stapel der internen Verzeichnisse. * Mit pushd Verzeichnis wird das angegebene Verzeichnis auf dem Stapel abgelegt.

  • Mit popd gelangen Sie anschließend zum zuletzt abgelegten Verzeichnis zurück.
  • Den aktuellen Stapelinhalt zeigt der Befehl dirs an.

5.7 Pfad-Cache

hash (ohne Parameter) zeigt die Liste der gemerkten Pfade zu den Programmen an.

Um die Zugriffe auf Programme zu beschleunigen, verwaltet die Bash einen internen Cache der Pfade auf bereits gestartete Programme.

Wird ein Programm erneut gestartet, kann die zeitaufwendige Suche entlang des Pfades entfallen.

Bei der Gelegenheit wird auch gleich angezeigt, wie oft das Programm gestartet wurde.

Wer am Abend wissen will, womit er sich den ganzen Tag über beschäftigt hat, kann mit diesem Kommando zumindest Hinweise bekommen (oder er wirft einen Blick auf die .bash_history)

5.7.1 Verwerfen alle gespeicherten Pfade

hash -r v

5.8 Verhalten interaktiver Shells

  • Bash liest Start-Dateien ein
  • Job Kontrolle ist eingeschaltet
  • Prompts sind gesetzt. PS2 ist aktiviert für mehrzeilige Kommandos (">"), wird auch angezeigt, wenn Kommandos nicht korrekt abgeschlossen wurden.
  • Kommandos werden mit readline von der Kommandozeile gelesen.
  • Bash beachtet die Shell-Option ignoreeof statt unmittelbar zu terminieren, wenn sie ein EOF (End Of File) empfängt.
  • Kommando-History und History-Expansion sind aktiviert
  • Alias-Expansion ist aktiviert
  • Ohne traps werden SIGTERM-Signale ignoriert
  • Ohne traps wird SIGINT beachtet und behandelt, Ctrl+C, beendet z.B. nicht die interaktive Shell
  • SIGHUP Signal an alle Jobs, beim Verlassen der Shell, kann mit der huponexit-Option konfiguriert werden
  • Kommandos werden nach dem Lesen ausgeführt.
  • Bash prüft periodisch nach neuen Mails
  • Bash terminiert nicht, wenn sie auf eine unreferenzierte Variable triff
  • redirection error interner Befehle terminiert die Shell nicht
  • Fehler interner Befehle im POSIX-Mode terminieren die Shell nicht
  • fehlerhafter Aufrufe mit exec terminieren die Shell nicht
  • Parser syntax errors terminiert die Shell nicht
  • einfacher spell check für die Argumente der internen Kommandos ist aktiviert
  • Automatisches exit nach der in der Variable TMOUT festgelegten Zeitspanne ist aktiviert

5.8.1 Ist die Shell interaktiv?

Bei Interaktiven Shells enthält der special parameter „–“ ein „i“, wenn die Shell interaktiv ist

echo $- himBH

In nicht-interaktiven Shells ist zudem die Prompt-Variable PS1 nicht gesetzt!

5.9 Verarbeitung der Kommandozeile

Im Abschnitt zur Syntax der Bash tauchte mehrfach der Begriff der Expansion auf. Im Sinne der Bash umfasst Expansion eine Menge von Regeln, nach denen die Eingabe in der Kommandozeile zunächst bearbeitet wird.

Jede durch Metazeichen getrennte Zeichenkette bildet ein einzelnes Wort in der Eingabe und wird von der Bash nach bestimmten Regeln expandiert.

Nachdem das Kommando in Token geteilt wurde, werden diese Wörter oder Token expandiert bzw. aufgelöst. Bestimmte Zeichenmuster werden hierbei durch andere substituiert - sie "expandieren".

Um zu versehen, wie die Shell mit einer Kommandozeile umgeht, ist es wichtig diese Expansionen zu kennen. In den nächsten Kapiteln werden alle Expansionen in Folge ihres Auftretens besprochen.

Welche Regeln wann und in welcher Reihenfolge zum Einsatz gelangen, hängt vom konkreten Kontext ab und wurde im Zusammenhang mit einfachen Kommandos (Expansion der Worte) und Variablenzuweisungen genannt.

Die Reihenfolge der Auswertung ist: # Kommando in Token aufteilen

  1. Erkennung von Schlüsselwörtern
  2. Auflösung von Aliasen
  3. Klammer-Expansion
  4. Tilde-Expansion
  5. Parameter- und Variablenexpansion
  6. Kommandosubstitution
  7. Arithmetische Substitution
  8. Wortaufteilung
  9. Pfadnamensexpansion
  10. Befehlssuche
  11. Befehl ausführen


5.9.1 Verarbeitung der Kommandozeile

Datei:Bild1.png

5.9.2 Verarbeitung der Kommandozeile
  1. Die Shell liest die Befehlszeile bis zum ersten Kommandotrenner (& && || ; > <) und zerlegt sie damit in einzelne Token.
  2. Hat die bash im ersten Token ein „öffnendes Schlüsselwort“ wie 'if', 'function' oder Ähnliches gefunden, liest Sie den nächsten Befehl ein. Wird ein anderes (nicht öffnendes Schlüsselwort gefunden, gibt sie eine Fehlermeldung aus. Andernfalls fährt sie mit Schritt drei fort.
  3. Hier werden Aliase (evtl. auch rekursiv) aufgelöst und durch ihren Inhalt ersetzt.
  4. Klammererweiterungen wie a{b,c} werden zu ab ac aufgelöst.
  5. Die Tilde wird durch den Inhalt von $HOME, ~user durch das Homeverzeichnis von user (z.B. /home/user) ersetzt.
  6. Alle Parameter (Variablen) mit einem Dollarzeichen ($) am Anfang werden durch ihren Wert ersetzt.
  7. Befehlsersetzungen der Form $(Befehle) oder `Befehl` werden durch ihr Ergebnis ersetzt.
  8. Berechnungen werden durch ihr Ergebnis ersetzt.
  9. Die Shell zerlegt 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.
  10. Metazeichen wie *,? oder […] werden durch ihr Ergebnis ersetzt.
  11. Alle Wörter werden nun als Befehl behandelt und zunächst als Funktion, dann als interner Befehl und zuletzt in allen in der $PATH-Variable enthaltenen Pfaden gesucht
  12. Nachdem die Ein-/Ausgabe-Umlenkung (und Ähnliches) eingerichtet wurde, führt die Shell die Befehlszeile aus. Sollte sich daraus eine neue Befehlszeile ergeben beginnt der Durchlauf evtl. erneut.

5.10 Aliase

5.10.1 Was sind Aliase?

Zur Abkürzung immer wiederkehrender Kommandofolgen lassen sich für diese so genannte Aliasse definieren.

Ein Alias erlaubt es, ein Wort durch ein einfaches Kommando vor der Ausführung der Kommandozeile ersetzen zu lassen.

Aliase sind nützlich um festzulegen welche Version eines Befehls ausgeführt werden soll oder um Optionen für die Ausführung von Befehlen festzulegen. Auch tippfeher können abgefangen ode korrigiert werden. alias weist einem Befehl oder einer Befehlsfolge einen neuen Namen zu. Beispiele für Windows-Umsteiger.

Die Shell unterhält eine Liste von Aliasen, die mit den internen Befehlen alias und unalias gesetzt und gelöscht werden können.

The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. The alias name and the replacement text may contain any valid shell input, including shell metacharacters, with the exception that the alias name may not contain "=".

The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one may alias ls to ls -F, for instance, and Bash will not try to recursively expand the replacement text.

If the last character of the alias value is a space or tab character, then the next command word following the alias is also checked for alias expansion. Aliases are not expanded when the shell is not interactive, unless the expand_aliases option is set using the shopt shell built-in.

Die Bash liest die gesamte Zeile, bevor ein Befehl aus der Zeile ausgeführt wird. Aliase werden expandiert, wenn der Befehl gelesen, nicht, wenn er ausgeführt wird.

Deshalb greifen Aliase, die in der gleichen Zeile definiert werden, erst in der nächsten Befehlszeile. Ein auf die Alias-Definition folgender Befehl wird von der Alias-Definition nicht beeinflusst. Dieses Verhalten spielt auch eine Rolle bei der Ausführung von Funktionen.

Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use alias in compound commands. Aliases are not inherited by child processes. Bourne shell (sh) does not recognize aliases.

5.10.2 Aliase auflisten (alias)

Alias ohne Option listet alle Aliase der aktuelle Shell auf:

alias alias ..='cd ..' alias ...='cd ../..' alias ....='cd ../../..' alias PAGER='less -r' alias Txterm='export TERM=xterm' alias XARGS='xargs -r' alias cdrecord='cdrecord -dev 0,0,0 -speed=8' alias e='vi' alias egrep='grep -E' alias ewformat='fdformat -n /dev/fd0u1743; ewfsck' alias fgrep='grep -F' alias ftp='ncftp -d15' alias h='history 10' alias j='jobs -l' alias ksane='setterm -reset' alias ls='ls -F --color=auto' alias m='less' alias md='mkdir' alias od='od -Ax -ta -txC' alias p='pstree -p' alias ping='ping -vc1' alias sb='ssh blubber' alias sl='ls' alias ss='ssh octarine' alias tar='gtar' alias tmp='cd /tmp' alias unaliasall='unalias -a' alias vi='eval `resize`;vi' alias vt100='export TERM=vt100' alias which='type' alias xt='xterm -bg black -fg white &'

Ein Alias existiert bis zum Ende der Shell, in der er definiert wurde oder bis zum expliziten Löschen mittels unalias:

unalias dir

unalias cdlin
cdlin
bash: cdlin: command not found

# ein Alias ist nur in der Shell seiner Definition bekannt:
bash
md tmp
bash: md: command not found

5.10.3 Alias erzeugen (alias)

Ein Alias wird mit dem Kommando alias erzeugt:

alias dir='ls -l'

alias md=mkdir
alias rd=rmdir
alias rename=mv
alias cdlin='cd /usr/src/linux' 

5.10.4 Aliase anwenden

Ein Alias wird genauso benutzt wie das entsprechende Kommando:

dir /

insgesamt 65
drwxr-xr-x   2  root     root         2048 Dec 14 13:23 bin
drwxr-xr-x   3  root     root         1024 Dec 21 10:59 boot
drwxr-xr-x   2  root     root         1024 Dec 14 13:05 cdrom
drwxr-xr-x   6  root     root        30720 Dec 29 08:50 dev
...
md directory
rd directory
rename nt refuse
cdlin; pwd
/usr/src/linux

5.10.5 Aliase löschen (unalias)

Zum Löschen eines Alias-Eintrags verwendet man den Befehl unalias Name.

Aliase werden von dem internen Kommando alias erzeugt.

Um sie dauerhaft zu speichern müssen sie in eine der Shell-Startdateien eingetragen werden.

Auf der Kommandozeile definierte Aliase sind nur in der akzuellen Shell bekannt und nicht persitent.

alias dh='df -h'

dh Filesystem Size Used Avail Use% Mounted on /dev/hda7 1.3G 272M 1018M 22% / /dev/hda1 121M 9.4M 105M 9% /boot /dev/hda2 13G 8.7G 3.7G 70% /home /dev/hda3 13G 5.3G 7.1G 43% /opt none 243M 0 243M 0% /dev/shm /dev/hda6 3.9G 3.2G 572M 85% /usr /dev/hda5 5.2G 4.3G 725M 86% /var

unalias dh

dh bash: dh: command not found

5.10.5.1 Funktionen sind schneller

Als Erstes sucht die Bash nach Funktionen, daher ist die Auflösung von Aliasen langsamer. Obwohl Aliase einfacher zu verstehen sind, werden Funktionen für die meisten Aufgaben vorgezogen.

6 Vorlage:Anchor Vorlage:Anchor Expansionen der Bash

6.1 Klammerexpansion (Brace expansion)

Mit Hilfe der Klammererweiterung lassen sich beliebige Zeichenketten generieren. Im einfachsten Fall verwendet man eine Präfix-Zeichenkette, gefolgt von beliebig vielen, mit geschweiften Klammern umschlossenen und durch Kommas getrennten Zeichen(ketten), wiederum gefolgt von einer optionalen Postfix-Zeichenkette.

Das Ergebnis sind nun Zeichenketten der Art "PräfixZeichenkette_1_Postfix", "PräfixZeichenkette_2_Postfix",..., "PräfixZeichenkette_n_Postfix".

An einem Beispiel lässt sich das Prinzip leicht verdeutlichen:

echo Beispiel{_1_,_2_,_3_}

Beispiel_1_ Beispiel_2_ Beispiel_3_

Präfix und Postfix können ihrerseits wiederum Klammererweiterungen sein und Klammererweiterungen lassen sich verschachteln, so dass sich z.B. mit nur einem Befehl eine ganze Verzeichnishierarchie erzeugen lässt:

mkdir -p bsp/{ucb/{ex,edit},lib/{bla,foo}}

du bsp | cut -b 3-
bsp/ucb/ex
bsp/ucb/edit
bsp/ucb
bsp/lib/bla
bsp/lib/foo
bsp/lib
bsp

Der Klammer-Mechanismus der eine große Menge an Zeichenketten erzeugen kann. Muster für die Klammerexpansion (brace-expanded) haben eine optionale PREAMBLE, gefolgt von einer kommaseparierten Liste von Zeichenketten zwischen einem Paar von Klammern gefolgt von einem optionalem POSTSCRIPT.

Die PREAMBLE wir jeder Zeichenkette vorangestellt, dass die Klammern enthalten, dass POSTSCRIPT wird an jede Ergebniszeichenkette abgehangen. Die Klammererweiterung kann auch verschachtelt werden. Das Ergebnis wird nicht sortiert, die links-zu-rechts-Ordnung bleibt erhalten.

echo sp{el,il,al}l spell spill spall

Die brace expansion wird vor allen anderen Expansionen ausgeführt und alle Zeichen, die für andere Expansionen eine besondere Bedeutung haben bleiben erhalten; es ist eine reine Textoperation. Die Bash führt keine syntaktische Interpretationen durch.

Um Konflikte mit der parameter expansion zu vermeiden, werden Zeichenketten die mit "${" beginnen, nicht als brace expansion verstanden.

Eine korrekt formulierte brace expansion muss eine unmaskierte öffnende und schließende Klammer besitzen und mindestens ein unmaskiertes Komma enthalten. Fehlerhaft formulierte brace expansion werden von der Bash nicht korrigiert.

6.1.1 Backup erstellen

cp -p /etc/apache2/plesk.conf.d/roundcube.conf{,.conf_back}

6.1.2 Brace expansion

Similar to filename expansion is brace expansion, which is a compact way of representing multiple similar arguments. The following four commands are equivalent:

ls file1.txt file2.txt file3.txt file4.txt file5.txt ls file{1,2,3,4,5}.txt ls file{1..5..1}.txt ls file{1..5}.txt

The first command lists each argument explicitly. The other three commands all use brace expansion to express the arguments more tersely: in the second command, all the possibilities 1 through 5 are given, separated by commas; in the third command, a numeric sequence is given ("from 1 to 5, incrementing by 1"); and the fourth command is the same as the third, but leaves the ..1 implicit.

We can also list the files in the opposite order:

ls file5.txt file4.txt file3.txt file2.txt file1.txt ls file{5,4,3,2,1}.txt ls file{5..1..-1}.txt ls file{5..1}.txt

with the default increment size being -1 when the endpoint of the sequence is less than the starting-point.

Since in Bash, the first word of a command is the program that is run, we could also write the command this way:

{ls,file{1..5}.txt}

but obviously that is not conducive to readability. (The same sort of thing, incidentally, can be done with filename expansion.)

Brace expansion, like filename expansion, can be disabled by any of the quoting mechanisms; '{', "{", or \{ produces an actual literal curly-brace.

6.2 Tildeexpansion

Beginnt der Wert mit einer unmaskierten Tilde (~), wird versucht, diese zu substituieren. Betrachtet werden alle der Tilde folgenden Zeichen bis zum ersten Schrägstrich (Slash). Ergibt dies eine gültige Benutzerkennung, so expandiert der Ausdruck zum Heimatverzeichnis dieses Benutzers.

Folgt der Tilde unmittelbar der Schrägstrich, wird der Ausdruck durch den Inhalt der Variablen HOME ersetzt; ist diese nicht gesetzt, wird das Heimatverzeichnis des aktuellen Benutzers angenommen:

var=~

echo $var
/home/user
var=~root/
echo $var
/root/

Wenn ein Wort mit einer unmaskierten tilde Zeichen ("~") beninnt,

All of the characters up to the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the tilde are treated as a possible login name.

If this login name is the null string, the tilde is replaced with the value of the HOME shell variable. If HOME is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name.

If the tilde-prefix is "~+", the value of the shell variable PWD replaces the tilde-prefix. If the tilde-prefix is "~-", the value of the shell variable OLDPWD, if it is set, is substituted.

If the characters following the tilde in the tilde-prefix consist of a number N, optionally prefixed by a "+" or a "-", the tilde-prefix is replaced with the corresponding element from the directory stack, as it would be displayed by the dirs built-in invoked with the characters following tilde in the tilde-prefix as an argument. If the tilde-prefix, without the tilde, consists of a number without a leading "+" or "-", "+" is assumed.

If the login name is invalid, or the tilde expansion fails, the word is left unchanged. Each variable assignment is checked for unquoted tilde-prefixes immediately following a ":" or "=". In these cases, tilde expansion is also performed.

Consequently, one may use file names with tildes in assignments to PATH, MAILPATH, and CDPATH, and the shell assigns the expanded value.

6.2.1 Beispiel

export PATH="$PATH:~/testdir"

~/testdir will be expanded to $HOME/testdir, so if $HOME is /var/home/dirkwagner, the directory /var/home/dirkwagner/testdir will be added to the content of the PATH variable.

6.3 Parameterexpansion

Folgt einem Dollarzeichen $ ein Variablenname oder eine öffnende geschweifte Klammer ${...}, so spricht man von einer Variablen- bzw. Parameterexpansion.

Die geschweiften Klammern dienen zur Gruppierung und sind bei skalaren Variablen, die nicht per Parameterexpansion behandelt werden sollen, nicht notwendig.

Beginnen wir mit einem Beispiel der Expansion einer skalaren Variable ohne Parameterexpansion:

var=user

var2=~$var
echo $var2
~user
eval echo $var2
/home/user

Bemerkung

Das Beispiel verdeutlicht die Reihenfolge der Auflösung bei Zuweisung eines Wertes an "var2".

Im ersten Schritt ist die Tilde nicht auflösbar, deshalb geht sie unverändert in "var2" ein.

In einem zweiten Schritt expandiert dann der Inhalt von "var", so dass "var2" nun "~user" beinhaltet.

Um den Expansionsmechnismus zu demonstrieren, wurde eine erneute Bewertung von "var2" erzwungen (eval); nun expandiert "~user" zum Heimatverzeichnis "/home/user".

Ist das erste Zeichen eines Parameters das Ausrufezeichen, so handelt es sich um eine indirekte Expansion.

Die Bash ersetzt den Ausdruck nun nicht mehr durch den Inhalt der Variablen, sondern betrachtet den Inhalt als den Namen einer Variablen, zu deren Inhalt nun expandiert wird.

Ein Beispiel erklärt den Sachverhalt wohl deutlicher, als es Worte vermögen:

var=user

var2=var
echo $var2
var
echo ${!var2}
user

Die weiteren Mechanismen zur Parameterexpansion manipulieren den Inhalt von Variablen.

Die Beispiele werden zeigen, dass diese Form der Substitution vor allem für die Shellprogrammierung von immensem Nutzen ist und genau dort werden sie uns wieder begegnen.

»parameter« bezeichnet nachfolgend den Variablennamen und »word« steht entweder für eine Zeichenkette oder für eine Variable, die selbst wieder eine Parameter-, Kommando, Tildeexpansion oder eine arithmetische Berechnung beinhalten kann.

6.3.1 Variablen-Substitution

Unter Variablen-Substitution versteht man verschiedene Methoden um die Inhalte von Variablen zu benutzen.

Das umfasst sowohl die einfache Zuweisung eines Wertes an eine Variable, als auch einfache Möglichkeiten zur Fallunterscheidung. In den fortgeschritteneren Shell-Versionen (bash, ksh)existieren sogar Möglichkeiten, auf Substrings von Variableninhalten zuzugreifen.

In der Standard-Shell benutzt man für solche Zwecke üblicherweise den Stream-Editor sed. Einleitende Informationen dazu finden sich im Kapitel über die Mustererkennung).

Die folgenden Mechanismen stehen in der Standard-Shell bereit, um mit Variablen zu hantieren. Bei allen Angaben ist der Doppelpunkt optional.

Wenn er aber angegeben wird, muss die Variable einen Wert enthalten.

Variable=Wert Setzt die Variable auf den Wert.
${Variable} Nutzt den Wert von Variable. Die Klammern müssen nicht mit angegeben werden, wenn die Variable von Trennzeichen umgeben ist.
${Variable:-Wert} Nutzt den Wert von Variable. Falls die Variable nicht gesetzt ist, wird der Wert benutzt.
${Variable:=Wert} Nutzt den Wert von Variable. Falls die Variable nicht gesetzt ist, wird der Wert benutzt, und Variable erhält den Wert.
${Variable:?Wert} Nutzt den Wert von Variable. Falls die Variable nicht gesetzt ist, wird der Wert ausgegeben und die Shell beendet. Wenn kein Wert angegeben wurde, wird der Text parameter null or not set ausgegeben.
${Variable:+Wert} Nutzt den Wert, falls die Variable gesetzt ist, andernfalls nichts.

Beispiele

$ h=hoch r=runter l= Weist den drei Variablen Werte zu, wobei l einen leeren Wert erhält.
$ echo ${h}sprung Gibt hochsprung aus. Die Klammern müssen gesetzt werden, damit h als Variablenname erkannt werden kann.
$ echo ${h-$r} Gibt hoch aus, da die Variable h belegt ist. Ansonsten würde der Wert von r ausgegeben.
$ echo ${tmp-`date`} Gibt das aktuelle Datum aus, wenn die Variable tmp nicht gesetzt ist. (Der Befehl date gibt das Datum zurück)
$ echo ${l=$r} Gibt runter aus, da die Variable l keinen Wert enthält. Gleichzeitig wird l der Wert von r zugewiesen.
$ echo $l Gibt runter aus, da l jetzt den gleichen Inhalt hat wie r.

Expansion vor der Wertzuweisung an eine Variable

Bevor ein Wert einer Variablen zugewiesen wird, versucht die Bash diesen Wert nach bestimmten Regeln zu substituieren. Dabei durchläuft die Bash folgende Schritte in beschriebener Reihenfolge: # Tildeexpansion

  1. Parameter- und Variablenexpansion
  2. Kommandosubstitution
  3. Arithmetische Substitution
  4. Entfernen der »Quoting«-Zeichen

Erst jetzt erfolgt die tatsächliche Zuweisung an die Variable. Was sich hinter den einzelnen Expansionen verbirgt, soll im Anschluss an diesen Abschnitt betrachtet werden.

Das "$" Zeichen leitet eine Parameterexpansion, Kommandosubstitution oder eine arithmetische Expansion ein. Der Parametername kann in Klammern gesetzt werden, um zu verhindern, dass direkt darauf folgende Zeichen als Teil des Namens interpretiert werden. Wenn Klammern benutzt werden ist die treffende Schließende Klammer die erste "}", die nicht durch einen maskiert wurde und sich nicht innerhaltb einer eingebetteten arithmetischen Expansion, eine Komanndosubstitution oder einer Parameter-Expansion befindet.

The basic form of parameter expansion is "${PARAMETER}". The value of "PARAMETER" is substituted. The braces are required when "PARAMETER" is a positional parameter with more than one digit, or when "PARAMETER" is followed by a character that is not to be interpreted as part of its name.

If the first character of "PARAMETER" is an exclamation point, Bash uses the value of the variable formed from the rest of "PARAMETER" as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of "PARAMETER" itself. This is known as indirect expansion. You are certainly familiar with straight parameter expansion, since it happens all the time, even in the simplest of cases, such as the one above or the following:

echo $SHELL /bin/bash

The following is an example of indirect expansion:

echo ${!N*} NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

Note that this is not the same as echo $N*. The following construct allows for creation of the named variable if it does not yet exist:

${VAR:=value}

Example:

echo $DIRKWAGNER

echo ${DIRKWAGNER:=Dirkwagner}
Dirkwagner

Special parameters, among others the positional parameters, may not be assigned this way, however.

6.4 Kommandosubstitution

Die Kommandosubstitution erlaubt das Ersetzen ihres Aufrufes durch ihre Ausgabe. Es existieren zwei Syntaxvarianten des Aufrufs:

$(Kommando)

`Kommando`

Die Bash führt das Kommando aus und ersetzt seinen Aufruf auf der Kommandozeile durch dessen Ausgabe, wobei abschließende Zeilenendezeichen entfernt wurden.

  1. ohne Kommandosubstitution
find / -name "whatis" 2>/dev/null | ls -l | head -5
insgesamt 15888
-rw-r--r--    1 user    users    787067 Apr  1 09:02 Buch.tar.gz
drwxr-xr-x    4 user    users      4096 Jan 16 19:49 Dhtml
drwx------    5 user    users      4096 Apr 26 09:48 Desktop
drwxr-xr-x    4 user    users      4096 Apr 21 08:43 IGLinux

# mit Kommandosubstitution
ls -l $(find / -name "whatis" 2>/dev/null)
ls -l $(find / -name "whatis"  2>/dev/null)
-rw-r--r--    1 root     root     94414 Jun 13 18:34 /usr/X11R6/man/whatis
-rw-r--r--    1 root     root    792270 Jun 13 18:34 /usr/man/allman/whatis
-rw-r--r--    1 root     root    220874 Jun 13 18:34 /usr/man/whatis
-rw-r--r--    1 root     root         0 Jun 13 18:34 /usr/openwin/man/whatis

Eine solche Kommandosubstitution kann auch bei der Zuweisung an eine Variable angewendet werden:

AktuellPfad=$(pwd)

echo $AktuellerPfad
/home/user

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)"

Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed like this:

$(command) or like this using backticks: `command`

Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

Embedded newlines are not deleted, but they may be removed during word splitting.

echo `date`

Thu Feb 6 10:06:20 CET 2003

When the old-style backquoted (accent aigu) form of substitution is used, backslash retains its literal meaning except when followed by "$", "`", or "\".

The first backticks not preceded by a backslash terminates the command substitution.

When using the "$(COMMAND)" form, all characters between the parentheses make up the command; none are treated specially.

Command substitutions may be nested. To nest when using the backquoted form, escape the inner backticks with backslashes.

If the substitution appears within double quotes, word splitting and file name expansion are not performed on the results.

6.4.1 Prozesssubstitution

Die Ein- bzw. Ausgabe von Prozessen kann mittels der Prozesssubstitution mit einer FIFO-Datei verbunden werden.

Taucht ein Konstrukt der Art <(Liste) bzw. >(Liste) auf, werden die durch Liste benannten Kommandos in einer Subshell gestartet. Gleichzeitig wird die Ausgabe (>(...)) bzw. Eingabe (<(...)) der Kommandos mit einer automatisch erzeugten FIFO-Datei verbunden.

Auf der Kommandozeile erscheint nach erfolgter Substitution der Name der erzeugten FIFO-Datei.

ls <(echo "hello")

/dev/fd/63

Mit Hilfe der Prozesssubstitution könnte man den vi dazu bewegen, die Ausgaben eines Kommandos zu lesen:

vi <(ls /boot/vm*)

/boot/vmlinuz
/boot/vmlinuz.old
~

~
"/dev/fd/63" [fifo/socket] 2L, 32C
                                       1,1            All

Ein weiteres Beispiel dient zur Bestandsaufnahme laufender Prozesse:

diff <(ps ax) <(sleep 10; ps ax)

64d63
<  2129 pts/0    S      0:00 /bin/bash
67,68c66
<  2132 pts/0    R      0:00 ps ax
<  2133 pts/0    S      0:00 sleep 10
---
>  2134 pts/1    S      0:00 top
>  2135 pts/0    R      0:00 ps ax

Im Beispiel ist der Prozess top neu hinzugekommen, dass die Aufrufe der Kommandos ps und sleep erscheinen, war zu erwarten.

Und abschließend vergleichen wir die Inhalte zweier Archive:

diff <(tar tzf Buch1.tar.gz) <(tar tzf Buch.tar.gz)

325a326,328
> Images/tkinfo.gif
> Images/GlobaleVariable.gif
> Images/LokaleVariable.gif

Innerhalb der Klammern >(...), <(...) können Parameter- Kommando- sowie arithmetische Substitutionen benutzt werden.

6.5 Arithmetische Substitution

Die Bash ist kein Taschenrechner. Dennoch besitzt sie ein erstaunliches Potenzial an eingebauten Rechenoperationen, die -- nach Prioritäten geordnet -- nachfolgende Tabelle zusammenfasst:

VAR++ and VAR-- variable post-increment and post-decrement
++VAR and --VAR variable pre-increment and pre-decrement
+ - Einstelliger Operator (Vorzeichen)
! ~ Logische und bitweise Negation
** Exponentialfunktion
* / % Multiplikation, Division und Modulo-Operator
+ - Addition, Subtraktion
<< >> Bitweise Links-/Rechtsverschiebung
<= >= < > Vergleiche
== != Gleichheit und Ungleichheit
& Bitweises UND
^ Bitweises Exclusive ODER
| Bitweises ODER
&& Logisches UND
Logisches ODER
expr ? expr : expr Bedingte Zuweisung
=, *=, /=, %=, +=, -= <<=, >>=, &=, ^=, |= Zuweisungen
, separator between expressions

Als Operanden sind Konstanten und Shellvariablen (deren Inhalt als long integer betrachtet wird) erlaubt. Beginnt eine Konstante mit "0", dann wird sie als oktale Zahl verstanden; steht "0x" am Anfang, handelt es sich um eine hexadezimale Konstante.

Konstanten können zu jeder Basis zwischen 2 und 64 angegeben werden, so kann die Zahl 63 u.a. wie folgt dargestellt werden: * Zur Basis 10: 10#63

  • Zur Basis 8: 8#77
  • Zur Basis 16: 16#3f

Die so genannte arithmetische Substitution ist der gebräuchliche Weg, um Berechnungen durchzuführen: * Bash Versionen <2: Der zu berechnende Ausdruck wird in eckigen Klammern geschrieben: $[...]

  • Bash ab Version 2: Der zu berechnende Ausdruck wird in doppelte runde Klammern geschrieben: $((...)) (die alte Syntax wird weiterhin unterstützt)

Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:

6.5.1 $(( EXPRESSION ))

The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially.

All tokens in the expression undergo parameter expansion, command substitution, and quote removal. Arithmetic substitutions may be nested.

Evaluation of arithmetic expressions is done in fixed-width integers with no check for overflow - although division by zero is trapped and recognized as an error.

The operators are roughly the same as in the C programming language. In order of decreasing precedence, the list looks like this:

Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated.

Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax.

The value of a variable is evaluated as an arithmetic expression when it is referenced. A shell variable need not have its integer attribute turned on to be used in an expression.

Constants with a leading 0 (zero) are interpreted as octal numbers. A leading "0x" or "0X" denotes hexadecimal. Otherwise, numbers take the form "[BASE'#']N", where "BASE" is a decimal number between 2 and 64 representing the arithmetic base, and N is a number in that base.

If "BASE'#'" is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, "@", and "_", in that order.

If "BASE" is less than or equal to 36, lowercase and uppercase letters may be used interchangably to represent numbers between 10 and 35.

Operators are evaluated in order of precedence. Sub-expressions in parentheses are evaluated first and may override the precedence rules above.

Wherever possible, Bash users should try to use the syntax with square brackets:

$[ EXPRESSION ]

However, this will only calculate the result of EXPRESSION, and do no tests:

echo $[365*24] 8760

6.5.2 Übungen

b=5; b=$((b+1)); echo $b

6

a=$((b+=10)); echo $a

16

echo $((a>b?1:0))

1

echo $((8#17**2))

225

echo $((017**2))

225

echo $((-0x64*3#11%6))

-4

echo $((4<<1))

8

Wird als Operand eine Variable benutzt, so wird versucht, deren Inhalt in eine Ganzzahl zu konvertieren. Enthält die Variable keine Zahl, wird der Inhalt zu "0" konvertiert:

b="Ist b keine Zahl, wird b zu 0 konvertiert"

echo $b
Ist b keine Zahl, wird b zu 0 konvertiert

b=$(($b+1)); echo $b

1

6.6 Funktionen

Eine Funktion ist ein Name für ein Kommando oder für eine Gruppe von Kommandos.

Funktionen werden vorrangig in Shellskripten verwendet, um wiederkehrende Kommandosequenzen nicht ständig neu schreiben zu müssen.

Ein großer Vorteil von Funktionen ist, dass sich diese in einer Datei speichern lassen und diese Datei von anderen Skripten geladen werden kann.

Eine Funktion wird wie folgt definiert:

Format: [function] Funktionsname() { Kommando; [Kommando;] }

Bei der Verwendung von Funktionen sind einige Regeln zu befolgen: * Deckt sich der Name der Funktion mit einem builtin-Kommando, wird immer die Funktion ausgeführt und niemals das Kommando. Ebenso verdeckt ein Funktionsname ein gleichnamiges Kommando:

type test

test is a shell builtin
test(){ echo "ich bin eine Funktion"; }
type test
test is a function
test ()
{
echo "ich bin eine Funktion"
}
unset test* Die Funktion muss vor ihrer Verwendung definiert sein. 
  • Eine Funktion läuft in der aktuellen Umgebung, d.h. alle Variablen der Umgebung sind sichtbar und alle Variablen, die in der Funktion definiert wurden, sind auch außerhalb sichtbar:

func(){ var_in_func=xxx; }

func
echo $var_in_func
xxx* Wird eine Funktion mittels »exit« verlassen, wird auch der rufende Prozess beendet:

func(){ exit; }

func
login:* Der Rückgabewert einer Funktion ist der Rückgabewert des letzten in ihr gestarteten Kommandos: 

func(){ grep -q foo /etc/passwd; echo $?; }

func
1
echo $?
0
func(){ return 255; }
func
echo $?
255* Funktionen sind nur in der Shell ihrer Definition bekannt: 

func(){ echo "lokal"; }

bash
func
bash: func: command not found
exit
func
lokal

Einer Funktion können Parameter als Argumente übergeben werden:

Aufruf: Funktionsname [Arg1] [Arg2]...

Innerhalb einer Funktion kann auf die Parameter mittels der Positionsparameter $1..$9 zugegriffen werden. Als Beispiel dient eine Funktion "square", die die als Argument übergebene Zahl quadriert:

square() { test -z $1 && return 1; expr $1 \* $1; }

square
square 18
324

Erklärung

Das builtin-Kommando test liefert den Status "0", falls die Variable "$1" leer ist (kein Argument).

In diesem Fall wird "return 1" ausgeführt und der Funktionsaufruf beendet. expr berechnet den Ausdruck und schreibt das Ergebnis auf die Standardausgabe.

6.7 Wortzerlegung

In diesem Schritt wird die Kommandozeile in so genannte Token unterteilt. Welche Zeichen als Separatoren verwendet werden, verrät die Variable $IFS (Internal Field Separator).

Ist diese nicht gesetzt, gelten die schon erwähnten Whitespaces als Begrenzer, sofern sie nicht innerhalb von (Doppel) Anführungsstrichen stehen oder durch den Backslash "maskiert" wurden.

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words on these characters.

If IFS is unset, or its value is exactly "'<space><tab><newline>'", the default, then any sequence of IFS characters serves to delimit words.

If IFS has a value other than the default, then sequences of the whitespace characters "space" and "Tab" are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character).

Any character in IFS that is not IFS whitespace, along with any adjacent IF whitespace characters, delimits a field.

A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.

Explicit null arguments ("""" or "") are retained. Unquoted implicit null arguments, resulting from the expansion of parameters that have no values, are removed. If a parameter with no value is expanded within double quotes, a null argument results and is retained.

Datei:Grafik29.png Expansion and word splitting
If no expansion occurs, no splitting is performed.

6.8 Expansion von Dateinamen

After word splitting, unless the -f option has been set , Bash scans each word for the characters "*", "?", and "[".

If one of these characters appears, then the word is regarded as a PATTERN, and replaced with an alphabetically sorted list of file names matching the pattern.

If no matching file names are found, and the shell option nullglob is disabled, the word is left unchanged.

If the nullglob option is set, and no matches are found, the word is removed.

If the shell option nocaseglob is enabled, the match is performed without regard to the case of alphabetic characters.

When a pattern is used for file name generation, the character "." at the start of a file name or immediately following a slash must be matched explicitly, unless the shell option dotglob is set.

When matching a file name, the slash character must always be matched explicitly. In other cases, the "." character is not treated specially.

The GLOBIGNORE shell variable may be used to restrict the set of file names matching a pattern.

If GLOBIGNORE is set, each matching file name that also matches one of the patterns in GLOBIGNORE is removed from the list of matches.

The file names . and .. are always ignored, even when GLOBIGNORE is set.

However, setting GLOBIGNORE has the effect of enabling the dotglob shell option, so all other file names beginning with a "." will match.

To get the old behavior of ignoring file names beginning with a ".", make ".*" one of the patterns in GLOBIGNORE. The dotglob option is disabled when GLOBIGNORE is unset.

6.8.1 Metazeichen und Globbing

Damit man beim Angeben von z. B. Dateinamen nicht alle Namen eintippen muss, sondern die Dateien auch alle oder nach bestimmten Kriterien auswählen kann, gibt es Metazeichen (Jokerzeichen, Wildcards).

Metazeichen sind Zeichen mit erweiterter Bedeutung

Expansion von Dateinamen

Im Gegensatz zu anderen Systemen (z. B. Windows) werden Metazeichen von der Shell durch alle passenden Dateinamen ersetzt, bevor sie die Befehlszeile ausführt. Dies führt zu einem für Windows-Anwender ungewohntem Verhalten.

Metazeichen werden von der Shell ersetzt

Vorteile* liegt darin, dass, dass nahezu jedes UNIX-Kommando als Dateiangabe eine beliebige Menge von Dateien als Parameter haben kann.

  • So sind in Programmen keine Systemaufrufe nötig, die auf die Verzeichnisinformation zugreifen; es wird lediglich eine Schleife benötigt, welche die einzelnen Dateien nacheinander bearbeitet.

Praktisches Globbing

Ganz schlecht

chmod a+r witzig1.jpg

chmod a+r witzig2.jpg
chmod a+r witzig3.jpg

Besser

chmod a+r witzig1.jpg witzig2.jpg witzig3.jpg

Super

chmod a+r *.jpg

Es gibt folgende Metazeichen

* Der Stern steht für eine beliebige Zeichenfolge - oder für überhaupt kein Zeichen.

"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".

[...] Im einfachsten Fall steht es für genau ein Zeichen aus der Menge (bspw. "[aeiou]" für einen Vokal).

Diese Angabe kann negiert werden ("alle außer diese Zeichen"), indem das erste Zeichen nach [ ein ! oder ^ ist ("[!abc]" bzw. "[^abc]").

Anstatt einzelne Zeichen aufzuzählen, lassen sich Bereiche angeben ("[a-z]" meint alle Kleinbuchstaben).

Wichtig!

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. 

6.8.2 Zeichenbereiche

Außer grep und Regulären Ausdrücken, gibt es eine gute Auswahl an pattern matching, dass die Shell direkt durchführen kann, ohne dass dafür externe Programme eingesetzt werden müssen.

Bekannt sind sicherlich das Sternchen (asterisk) „*“ und das Fragezeichen (question mark) „?“, die jede Zeichenkette oder jeden Buchstaben filtern. Um diese Sonderzeichen als Literale zu filtern müssen sie maskiert werden:

touch "*" ls "*"

But you can also use the square braces to match any enclosed character or range of characters, if pairs of characters are separated by a hyphen. An example:

ls -ld [a-cx-z]* drwxr-xr-x 2 dirkwagner dirkwagner 4096 Jul 20 2002 app-defaults/ drwxrwxr-x 4 dirkwagner dirkwagner 4096 May 25 2002 arabic/ drwxrwxr-x 2 dirkwagner dirkwagner 4096 Mar 4 18:30 bin/ drwxr-xr-x 7 dirkwagner dirkwagner 4096 Sep 2 2001 crossover/ drwxrwxr-x 3 dirkwagner dirkwagner 4096 Mar 22 2002 xml/

This lists all files in dirkwagner's home directory, starting with "a", "b", "c", "x", "y" or "z".

If the first character within the braces is "!" or "^", any character not enclosed will be matched.

To match the dash ("-"), include it as the first or last character in the set.

The sorting depends on the current locale and of the value of the LC_COLLATE variable, if it is set.

Mind that other locales might interpret "[a-cx-z]" as "[aBbCcXxYyZz]" if sorting is done in dictionary order.

If you want to be sure to have the traditional interpretation of ranges, force this behavior by setting LC_COLLATE or LC_ALL to "C".

6.8.3 Nullglob

Ohne Match wird das Argument unverändert übernommen. Dieses Verhalten lässt sich mit der Shelloption nullglob beeinflussen.

Match - gefundene Dateien werden übernommen

echo a1*.jpg

a1.jpg a10.jpg a11.jpg

Kein Match - Argument bleibt unverändert

echo b1*.jpg

b1*.jpg

6.8.4 Dotfiles

Der Punkt am Anfang von Dateinamen stellt eine Ausnahme dar, er muss explizit angegeben werden (wegen der Verzeichnisreferenzen "." bzw. ".." und der Tatsache, dass Dateien, die mit einem Punkt beginnen, normalerweise nicht angezeigt werden). * Dateien, die mit "." beginnen sind versteckt

  • Shell Globbing ignoriert die "Dotfiles"
  • Punkt muss explizit genannt werden

echo .*

. .. .bash_history .bash_profile .bashrc

6.8.5 Globbing und Brace Expansion

Brace Expansion hat starke Ähnlichkeit mit Globbing doch die angegebenen Dateinamen müssen nicht existieren.

ls

a1.jpg a10.jpg a11.jpg a2.jpg a3.jpg a4.jpg

"{name1,name2}" - Alternativen aufzählen

echo a{2,10}.jpg

a2.jpg a10.jpg

6.8.6 Zeichenklassen

Zeichenklassen können mit folgender Syntax in eckigen Klammen angegeben [:CLASS:]. Dabei wird CLASS nach POSIX-Standard angegeben und kann folgende Werte annehmen:

"alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word" or "xdigit"

ls -ld digit:* drwxrwxr-x 2 dirkwagner dirkwagner 4096 Apr 20 13:45 2/

ls -ld upper:* drwxrwxr-- 3 dirkwagner dirkwagner 4096 Sep 30 2001 Nautilus/ drwxrwxr-x 4 dirkwagner dirkwagner 4096 Jul 11 2002 OpenOffice.org1.0/ -rw-rw-r-- 1 dirkwagner dirkwagner 997376 Apr 18 15:39 Schedule.sdc

Wenn die Shell-Option extglob aktiviert ist, werden erweiterte pattern matching Operatoren erkannt.

6.8.7 Extglob (erweitertes Globbing)

Die ksh88 führte zusätzlich die erweiterte Mustererkennung ein. Diese sind vergleichbar mit der Leistung Regulärer Ausdrücke, die Notation ist ähnlich, aber nicht gleich. Wenn die Shelloption extglob eingeschaltet ist, unterstützt auch die Bash diese Optionen.

Wenn möglich, sollte die erweiterte Mustererkennung regulären Ausdrücken vorgezogen werden, da diese effizienter und damit deutlich schneller ausgewertet werden können. Die Muster sind eine Liste von Zeichenketten, die durch | getrennt sind.

Das Zeichen vor der öffnenden Klammer reguliert die Auswertung des Musters:

?(Muster-Liste) Kein oder ein Auftreten eines Musters
*(Muster-Liste) Kein oder mehrere Auftreten eines Musters
+(Muster-Liste) Ein oder mehrere Auftreten eines Musters
@(Muster-Liste) Genau ein Auftreten eines Musters
!(Muster-Liste) Alle außer den angegebenen Mustern

Die erweiterte Mustererkennung kann mit Standard-Shell-Wildcards kombiniert und verschachtelt werden.

6.8.8 Übungen 1

  1. Vorbereitung

mkdir ~/gob && cd ~/gob

touch a aa aaa b bb bbb c cc ccc d dd ddd# Beliebig viele beliebige Zeichen (*)

ls a*

a  aa  aaa# Ein beliebiges Zeichen (?)

ls a?

aa# Ein Zeichen aus einer Menge [...]

ls [abc]

a  b  c# Ein Zeichen aus einem Bereich [ - ]

ls [a-d]

a  b  c  d# Kein Zeichen aus der Menge [! ]

ls [!ab]*

c  cc  ccc  d  dd  ddd# Kein Zeichen aus einem Bereich [! - ]

ls [!a-c]*

d  dd  ddd

6.8.9 Übungen 2

  1. Vorbereitung:

mkdir ~/extgob && cd ~/extgob

touch 1 11 111 2 22 222 3 33 333 
touch a aa aaa b bb bbb c cc ccc# ?(Muster-Liste)- kein oder ein Auftreten eines Musters

ls ?(a|b)

a  b# *(Muster-Liste) - kein oder mehrere Auftreten eines Musters

ls *(a|b)

a  aa  aaa  b  bb  bbb# +(Muster-Liste) - ein oder mehrere Auftreten eines Musters

ls +(a|b)

a  aa  aaa  b  bb  bbb# @(Muster-Liste) - genau ein Auftreten eines Musters

ls @(a|b)

a  b# !(Muster-Liste)- alle außer den angegebenen Mustern 

ls !(a|b)

1  11  111  2  22  222  3  33  333  aa  aaa  bb  bbb  c  cc  ccc# .!(|.)  - Dotfiles ohne "." und ".."

ls -d .!(|.)

.bash_history .bash_profile .bashrc# Zeichenklasse:

ls alpha:

a  b  c# Kombinationen

ls +(digit:)

1  11  111  2  22  222  3  33  333

ls ?(digit:)

1  2  3

ls +(digit:|!(upper:))

1  11  111  2  22  222  3  33  333  a  aa  aaa  b  bb  bbb  c  cc  ccc

7 Syntax der Bash

Datei:Grafik4.png

7.1 Definitionen

Metazeichen Ein Zeichen, das einzelne Worte trennt: | & ; () < > Leerzeichen Tabulator
Name Zeichenkette, bestehend aus alphanumerischen Zeichen und dem Unterstrich, wobei das erste Zeichen keine Ziffer ist.
Kontrolloperator Ein Token, das eine spezielle Kontrollfunktion auslöst, die Symbole sind: | & && ; ;; () Zeilenumbruch
Token Eine Zeichenfolge, die von der Shell als eine Einheit betrachtet wird.
Whitespace Steht für Leerzeichen und Tabulatoren und ggf. dem Zeilenumbruch.

7.2 Einfache Kommandos

Ein einfaches Kommando ist ein Ausschnitt der Kommandozeile, den die Bash in einem Zusammenhang betrachtet. Der Begriff des »einfachen Kommandos« definiert einen wichtigen Aspekt der Semantik der Bash.

Dazu untersucht die Shell die Eingabe und extrahiert alle enthaltenen »einfachen Kommandos«, für die sie nachfolgend definierte Substitutionen vollzieht.

Ein einfaches Kommando wird stets durch einen Kontrolloperator abgeschlossen.

Es besteht aus einer Sequenz optionaler Variablenzuweisungen, von durch Whitespaces getrennten Worten und Ein-/Ausgabe-Umleitungen. Das erste Wort, das keine Variablenzuweisung ist, wertet die Bash als Kommandoname, alle weiteren Worte sind die Argumente des Kommandos.

Man kann sich als einfaches Kommando den Abschnitt der Kommandozeile vorstellen, der innerhalb ein und desselben Prozesses ausgeführt wird.

Die einzelnen Substitutionsschritte, die die Bash für jedes einfache Kommando vornimmt, sind: # Variablenzuweisungen und Ein-/Ausgabe-Umleitungen werden markiert (damit sie nicht im folgenden Schritt betrachtet werden)

  1. Alle nicht-markierten Worte werden expandiert (siehe nachfolgend)
  2. Ein- und Ausgabe-Umleitungen werden vorbereitet (indem die entsprechenden Dateideskriptoren geschlossen/geöffnet werden)
  3. Die Variablenzuweisungen werden vorgenommen (inklusive vorheriger Expansionen)

7.3 Rückgabewerte

Mit Beendigung eines Kommandos liefert dieses einen Statuswert an seinen Elternprozess.

Dieser Wert kann eine Zahl zwischen 0 und 255 beinhalten und gibt Auskunft über den Erfolg (Rückgabewert »0«) und Misserfolg (Rückgabewert »> 0«) der Ausführung des Kommandos.

Viele Kommandos kodieren im Wert die vermutliche Ursache des Scheiterns, wobei Werte zwischen 0 und 127 empfohlen sind.

Wird ein Kommando »von außen« beendet (kill), so ist der Rückgabewert die Signalnummer + 128. Die Shell speichert den Rückgabestatus des letzten Kommandos in der speziellen Variable $?.

grep root /etc/passwd

root:x:0:0:root:/root:/bin/bash
echo $?
0
grep bla /etc/passwd
echo $?
1
grep root /ETC/passwd
grep: /ETC/passwd: Datei oder Verzeichnis nicht gefunden
echo $?
2

7.4 Shell-Sonderzeichen und Maskierung

7.4.1 Shell-Sonderzeichen

In der Bash gibt es verschiedene Zeichen, die eine besondere Bedeutung haben.

Maskieren (Quoting) dient dazu, diesen Shell-Sonderzeichen vor der Shell zu 'verstecken', ihnen ihre besondere Bedeutung zu nehmen, um z.B. zu verhindern, dass diese expandiert (ersetzt) werden.

Viele Zeichen haben eine besondere Bedeutung je nach Kontext.

Die wichtigsten Shell-Sonderzeichen

; Befehls-Trennzeichen
& Hintergrund-Verarbeitung
( ) Befehls-Gruppierung
{ } Klammersubstitution
| Pipe
< > & Umlenkungssymbole
* ? [ ] ~ + - @ ! Meta-Zeichen für Dateinamen
` ` (Backticks) Befehls-Substitution
$ Variablen-Substitution
[newline] [space] [tab] Wort-Trennzeichen (IFS)

7.4.2 Maskierung (quoting)

7.4.2.1 Einzelne Zeichen maskieren (escape characters)

Escape characters werden benutzt, um einem einzelnen Zeichen seine spezielle Bedeutung zu nehmen. Dazu wird in der Bash ein nicht maskierter Backslash \ benutzt.

Es erhält den literalen Wert des folgenden Zeichens mit Ausnahme von newline.

Wenn ein newline Zeichen direkt nach einem Backslash auftaucht, markiert es die Fortsetzung des Befehls in der nächsten Kommandozeile; der Backslash wird aus dem input stream entfernt und tatsächlich ignoriert.

Beispiel

echo $USER

dirkwagner

echo \$USER

$USER
7.4.2.2 Keinerlei Substitutionen (Single quotes)

Einfache Anführungszeichen (' ') werden genutzt um den literalen Wert aller eingeschlossenen Zeichen zu erhalten.

Die eingeschlossene Zeichenkette darf kein einfaches Anführungszeichen enthalten, selbst wenn es mit einem Backslash maskiert wird.

Beispiel

echo 'Weder * noch $(pwd) werden ersetzt'

Weder * noch $(pwd) werden ersetzt
7.4.2.3 Selektive Substitutionen (Double quotes)

Bei der Benutzung doppelter Anführungszeichen behalten alle Zeichen ihre literale Bedeutung mit Ausnahme des Dollarzeichens, der backticks (backward single quotes, ``) und dem Backslash. Sie behalten Ihre spezielle Bedeutung.

Der Backslash behält seine Bedeutung nur, wenn ein Dollarzeichen, ein Backtick doppelte Anführungszeichen, ein Backslash oder newline folgen. Bei doppelten Anführungszeichen wird der Backslash aus dem Eingabedatenstrom entfernt, wenn eines dieser Zeichen folgt.

Backslashes preceding characters that don't have a special meaning are left unmodified for processing by the shell interpreter.

Doppelte Anführungszeichen können innerhalb von doppelten Anführungszeichen durch einen Backslash maskiert werden

echo "$date" 20021226

echo "`date`" Sun Apr 20 11:22:06 CEST 2003

echo "I'd say: \"Go for it!\"" I'd say: "Go for it!"

echo "\" More input>"

echo "\\" \

Keine Ersetzung der Metazeichen, jedoch Variablen- und Befehlssubstitution

echo Der * wird hier durch alle Dateinamen ersetzt echo "Der * wird hier nicht ersetzt"

7.4.2.3.1 ANSI-C quoting

Ausdrücke der Art "$'STRING'" werden auf eine spezielle Weise behandelt.

Der Ausdruck expandiert zu einer Zeichenkette mit Backslash-maskierten Zeichen wie es im ANSI-C -Standard festgelet ist. Backslash-maskierte Sequenzen können der Bash-Dokumentation entnommen werden.

7.4.2.3.2 Locales

Eine Zeichenkette in doppelten mit vorangestelltem Dollarzeichen wird in die aktuelle Landeseinstellung (locale) übersetzt.

Wenn die aktuelle Landeseinstellung „C“ oder „POSIX“ ist, wird das Dollarzeichen ignoriert. Wurde die Zeichenkette übersetzt und ersetzt, ist die Ersetzung mit doppelten Anführungszeichen maskiert.

7.5 Kommentare

Normalerweise leitet das Doppelkreuz # in der Bash einen Kommentar ein.

Alles, was diesem Zeichen folgt, wird von der Bash ignoriert bis zum nächsten Zeilenumbruch. Dies gilt sowohl für die interaktive als auch für die nicht-interaktive Bash.

Eine Ausnahme sind Shellskripte, in denen die erste Zeile mit #!... beginnt. Für die Bash ist es die Anweisung, die nachfolgenden Zeilen mit dem hinter #! angegebenen Interpreter (eine Shell, Perl, awk,...) auszuführen.

Die Bedeutung von # kann entweder durch maskieren oder durch die Shelloption "interactive_comments" aufgehoben werden.

cat \#script

#!bin/sh
echo "sinnlos"
#script

# Wirkung des Doppelkreuzes aufheben:
shopt -u interactive_comments
#script
sinnlos
# Wirkung des Doppelkreuzes aktivieren:
shopt -s interactive_comments

7.5.1 Kommentare einfügen

Kommentare sind wichtig und man sollte regen Gebrauch davon machen.

An schwierigen Passagen sollte man kleine Notizen hinterlegen um festzuhalten, was man sich dabei gedacht hat und wie das Konstrukt funktioniert.

So erleichtert man sich selbst, z.B. nach längeren Pausen, oder aber auch Anderen, welche das Skript bearbeiten möchten, das Verständnis.

Zudem lassen sich Kommentare auch gut als strukturgebendes Element einsetzen, was eine höhere Übersichtlichkeit ermöglicht.

Einfügen lassen sich Kommentare durch das Hash-Zeichen (#). Dies kann überall in einer Zeile stehen; die Bash ignoriert alles, was dahinter steht. Im Allgemeinen sieht das dann so aus.

  1. Dies ist ein Kommentar
kein Kommentar # Dies ist ein Kommentar.

### Auf diese Weise kann man beispielsweise Abschnitte trennen ###

7.6 Ein- und Ausgabeumleitung

Die drei dem Terminal zugeordneten 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.

Datei:Grafik2.png

Vorlage:Clear

7.6.1 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.

Datei:Grafik32.png

Kommando < Dateiname

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

mail tux@melmac.all -s Wichtig < Message.txt

7.6.2 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 vorhanden war, wird sie automatisch angelegt. Falls die Datei schon vorhanden ist, wird sie überschrieben, d. h. es wird immer ab dem Dateianfang geschrieben.

Datei:Grafik10.png
Kommando > Dateiname

Fehlermeldungen (stderr) erscheinen nach wie vor auf dem Bildschirm. Beispiel

Datei:Grafik22.png

ls -l > info

# Umleitung der Ausgabe von "ls -lt" in eine Datei
ls -lt > time_sorted

# Provozieren eines Fehlers
touch /etc/passwd
touch: /etc/passwd: Keine Berechtigung

# Umleitung des Fehlers
touch /etc/passwd 2> /dev/null

7.6.3 Überschreiben existierender Dateien verhindern

Die Shelloption "noclobber" verhindert das Überschreiben existierender Dateien:

set -o noclobber

ls -lt > time_sorted
bash: time_sorted: cannot overwrite existing file

"noclobber" zurüsetzen

set +o noclobber

7.6.4 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:

Datei:Grafik3.png

Datei:Grafik9.png

Kommando < Eingabedatei > Ausgabedatei 2> Fehlerdatei

7.6.5 Unterdrückung der Ausgabe

Durch Umleitung von Ein- und Ausgabe lässt sich auch unterdrücken. Für die Ausgabe schreibt man

Kommando > /dev/null

oder für die Fehlerausgabe

Kommando 2> /dev/null

Beides lässt sich auch kombinieren:

Kommando > Ergebnisdatei 2> /dev/null

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

Kommando < /dev/zero

7.6.6 Anhängen von Daten 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

Beispiele

Dateiliste und aktive Benutzer in einen Datei schreiben:

ls -l > liste

who >> liste 

7.6.7 Zusammenfassung von Datenströmen

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:

&0 Standardeingabe
&1 Standardausgabe
&2 Standard-Fehlerausgabe

Beispiel

Kommando > ausgabe 2>&1

7.7 Pipelines

In einer Pipeline werden mehrere Kommandos mit dem Pipesymbols | verknüpft.

Jedes Kommando wird in einer eigenen Prozessumgebung ausgeführt und über einen temporären Puffer mit dem Nächsten verbunden.

Das Kommando links eines Pipesymbols schreibt seine Ergebnisse in die Pipe (»Röhre«) anstatt auf die Standardausgabe.

Datei:Grafik11.png

Ein rechts des Symbols stehendes Kommando bezieht seine Eingabedaten aus der Pipe.

Die Pipeline ist somit eine wichtige Unterstützung des Unix-Werkzeugkasten-Prinzips durch die Bash, womit durch geschickte Kombination der scheinbar simplen Kommandos komplexe Aufgaben lösbar werden.

Kommando 1 | Kommando 2

Es 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.

Beispiel

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

ls -Rl / | less

Eine komplexere Anwendung ist die nächste Verknüpfung zweier tar-Befehle, womit ein ganzes Verzeichnis bei Beibehaltung der Eigentümer kopiert wird:

tar cf -./bla | (cd /zielpfad; tar xf -)

7.7.1 Filter

7.7.1.1 head

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.

7.7.1.2 tail

tail [-/+n] [bc[f|r]] [datei]

Optionen

+n ab der n. Zeile ausgeben
-n die 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 Strg-C-Taste). Damit kann man z. B. Logfiles beobachten, die ständig wachsen.
7.7.1.3 tee
tee liest von der Standardeingabe und verzweigt die Ausgabe auf die Standardausgabe und Datei.

Wird auf eine existierende Datei verzweigt, so wird sie überschrieben, anderenfalls wird sie angelegt.

Datei:Grafik14.png

tee [-ai] [-append] [-ignore-interrupts] [Datei ...]

7.7.1.3.1 Beispiel

make -k 2>&1 | tee make.out

7.7.1.3.2 Optionen
-a die Datei wird nicht überschrieben, sondern die Ausgabe daran angehängt
-i ignoriert Interrupt Signale
7.7.1.4 wc

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.

7.7.1.4.1 Optionen
-l Zähle Zeilen
-w Zähle Worte
-c Zähle Zeichen

Weitere Filter sind ...

7.8 Kommandoverkettungen

Die bash unterstützt mehrere Möglichkeiten, mehrere Kommandos nacheinander und in Abhängigkeit voneinander zu starten:

ls; date

Die Kommandofolge führt zunächst das Kommando ls aus und zeigt dann das aktuelle Datum an.

ls; date > datei

In datei steht das aktuelle Datum.

(ls; date) > datei

In datei stehen nun der Verzeichnisinhalt und das aktuelle Datum. Die Klammerung bewirkt die Ausführung der eingeschlossenen Kommandos in derselben Shell, sodass diese ein Ergebnis zurückliefern.

7.8.1 Übersicht

komm1; komm2 Führt die Kommandos nacheinander aus
komm1 && komm2 Führt komm2 nur aus, wenn komm1 erfolgreich war
komm1 komm2 Führt komm2 nur aus, wenn komm1 einen Fehler liefert
komm1 & Führt Kommando als Hintergrundprozess aus
komm1 & komm2 Startet komm1 im Hintergrund, komm2 im Vordergrund
(komm1; komm2) Startet beide Kommandos in einer Shell

7.8.2 Kommando_1; Kommando_2

Führt die Kommandos nacheinander aus. Kommandos, die unabhängig voneinander arbeiten, lassen sich somit bequem durch Semikola getrennt angeben. Das jeweils nächste Kommando wird unverzüglich gestartet, sobald das vorherige terminierte.

7.8.3 Kommando_1 && Kommando_2

Führt komm2 nur aus, wenn komm1 erfolgreich war.

7.8.4 Kommando_1 || Kommando_2

Führt Kommando_2 nur aus, wenn Kommando_1 erfolgreich (&&) bzw. nicht erfolgreich (||) war.

Insbesondere in Shellskripten wünscht man oft die bedingte Ausführung, d.h. ein Kommando soll in Abhängigkeit vom (Miss)Erfolg eines anderen Kommandos gestartet werden.

Als Beispiel soll eine Datei mit dem Kommando rm gelöscht werden.

Damit rm allerdings keine Fehlermeldung erzeugt, soll es nur ausgeführt werden, wenn wir die Berechtigung dafür besitzen und die Datei auch existiert.

Eine Erfolgsmeldung kann auch nicht schaden... Der Aufruf könnte wie folgt formuliert werden:

test -w bla && rm bla && echo "Datei entfernt"

Vielleicht sollte die Datei»bla«zur Demonstration des Beispiels angelegt werden? Allerdings nur, wenn sie noch nicht existiert:

test -e bla || touch bla

test -w bla && rm bla && echo "Datei entfernt"
Datei entfernt

7.8.5 Kommando &

Führt das Kommando als Hintergrundprozess aus.

Schickt man ein Kommando in den Hintergrund (indem ihm ein & nachgestellt wird), so wartet die Shell nicht mehr aktiv auf dessen Terminierung, sondern schreibt sofort wieder das Prompt aus und nimmt neue Eingaben entgegen.

Das im Hintergrund tätige Kommando ist von der Standardeingabe abgeschnitten. Einzig die Ausgaben landen weiterhin auf dem Bildschirm.

sleep 100 &

[1] 956

Die Ausgabe betrifft die Job- und die Prozessnummer des Hintergrundprozesses (1 bzw. 956).

Irgendwann wird der Prozess seine Arbeit erledigt haben, dann erscheint mit der nächsten Eingabe folgende Ausschrift:

"beliebige Eingabe" [Enter]

[1]+  Done             sleep 100

Folgt dem & ein weiteres Kommando, wird dieses sofort gestartet.

Somit lassen sich Kommandos quasi parallel ausführen (zur Demonstration greifen wir auf die Verwendung von Subshells zurück):

(sleep 6; echo -n "[1] "; date) & (echo -n "[2] "; date)

[1] 14914
[2] Die Okt 17 13:58:45 MEST 2000
[1] Die Okt 17 13:58:51 MEST 2000

7.8.6 { Kommando;}

Startet das Kommando innerhalb der aktuellen Shell. Sinn dieses Konstrukts ist die Gruppierung mehrerer Kommandos, sodass sie eine gemeinsame Ausgabe erzeugen:

ls -l | head -1; date > bla

insgesamt 763
cat bla
Die Okt 17 18:40:40 CEST 2000
{ ls -l | head -1; date;} > bla
cat bla
insgesamt 763
Die Okt 17 18:40:40 CEST 2000

Die erste Kommandozeile leitet nur die Ausgabe von date in die Datei »bla« um, deshalb landet die Ausgabe von »ls -l | head -1« auf der Standardausgabe. Die zweite Zeile gruppiert beide Kommandos, sodass die Ausgabe komplett in die Datei umgeleitet wird.

7.8.7 (Kommando)

Startet das Kommandos innerhalb einer neuen Shell. Die letzte Variante zur Eingabe einer Kommandosequenz betrifft deren Ausführung in einer Subshell.

D.h. die Bash startet als Kindprozess eine weitere Shell und übergibt dieser die auszuführenden Kommandos. Das Verhalten ähnelt stark der Gruppierung mittels geschweifter Klammern und tatsächlich lassen sich dieselben Wirkungen erzielen:

pwd; date > output

/home/user
cat output
Don Jun  8 09:38:23 MEST 2000

(pwd; date) > output
cat output /home/user
Don Jun  8 09:38:27 MEST 2000

Aus Effizienzgründen sollte, wann immer es möglich ist, auf die Subshell verzichtet werden (Erzeugen eines Prozesses kostet immer Zeit).

Notwendig kann sie allerdings werden, wenn verschiedenen Kommandos auf dieselben lokalen Variablen zugreifen. Durch Start in einer eigenen Shell sind Wechselwirkungen ausgeschlossen.

Der Rückgabewert einer Subshell ist immer »0« und die in ihr bearbeiteten Kommandos haben keinen Einfluss auf den Elternprozess.

So kann eine Subshell verwendet werden, um ein Kommando in einem anderen Verzeichnis auszuführen, ohne dass das Verzeichnis in der aktuellen Shell gewechselt wird:

pwd

/home/user
(cd /usr/X11R6/bin; pwd)
/usr/X11R6/bin
pwd
/home/user

7.9 Variablen

Im Sinne der Syntax werden skalare und Feldvariablen unterschieden.

7.9.1 Erzeugen von Variablen

variablen_name=wert

7.9.2 Variablen löschen

Hierzu dient das builtin-Kommando unset.

unset a

bash: unset: a: cannot unset: readonly variable
unset b
declare -p b
bash: declare: b: not found

7.9.3 Erzeugen einer Feldvariable (Array)

feld_var_name[index]=wert

# oder
feld_var_name=(wert1 wert2 wert3)

Den Inhalt einer Variable betrachten Sie mit Hilfe des Kommandos echo :

echo $variablen_name

wert
echo $feld_var_name
wert1
echo $feld_var_name[2]
wert1[2]
echo ${feld_var_name[2]}
wert3

Wenn Sie auf den Inhalt einer Feldvariable zugreifen, müssen Sie durch Klammerung klar stellen, dass Sie den Inhalt eines konkreten Elementes meinen.

Ohne explizites Setzen der Klammern wird als Index implizit »0« angenommen; daraus resultiert die Ausgabe von »echo $feld_var_name[2]«.

7.9.4 Variable-Attribut

Hierzu verwenden Sie declare oder typeset. Beide Kommandos besitzen die selben Optionen und Wirkungen.

Vorsicht : Entgegen aller Logik setzt »-« ein Attribut und »+« löscht es!

-a Setzt das Feldattribut einer Variable (wird einer Variable ein Feld zugewiesen, wird das Attribut automatisch gesetzt)
[-/+] f Zeigt eine Funktionsdefinition an/zeigt sie nicht an:

first_func() {echo "erste Funktion";}

first_func
erste Funktion
declare -f first_func
first_func()
{
echo "erste Funktion"
}
[-/+] i Setzt/Löscht das Integer-Attribut. Für so deklarierte Variablen ist eine einfachere Syntax für Arithmetik erlaubt.

declare -i a=3

b=3
a=a+b
b=a+b
echo $a "   " $b
6    a+b
-p Zeigt die Attribute und den Inhalt einer Variablen an.

declare -p a

declare -i a="6"
declare -p SHELL
declare -x SHELL="/bin/bash"
[-/+] r Setzt das Leseattribut/verwirrt den Leser. Ist es aktiv, kann der Wert einer Variablen nicht verändert werden. Die Variable kann weder gelöscht, noch kann ein Lese-Attribut entfernt werden:

declare -r a

a=nix
bash: a: readonly variable
declare +r a
bash: declare: a: readonly variable
[-/+] x Setzt/Löscht das export-Attribut. Eine exportierte Variable ist auch in den Nachfahren des die Shell ausführenden Prozesses sichtbar (Siehe unter Shellvariablen)

7.9.5 Gültigkeitsdauer von Variablen

7.9.5.1 Globale Shell-Variablen
Wie wir im einführenden Beispiel gesehen haben, dienen Shellvariablen dazu, das Verhalten bestimmter Programme zu beeinflussen.

Eine globale Variable ist ab der Shell ihrer Einführung sichtbar, also auch in allen daraus abgeleiteten Shells und in allen darin gestarteten Programmen.

Bereits beim Systemstart werden einige Variablen global belegt. So zum Beispiel PATH, die die Suchpfade der Programme enthält oder WINDOWMANAGER, die Variable, die den Default-Windowmanager setzt.

Datei:Grafik13.png

Manchmal entsprechen die vom Administrator getroffenen Einstellungen nicht den Vorstellungen des Nutzers. So wird dieser die Variablen überschreiben.

Um die Variablen auch außerhalb der aktiven Shell sichtbar zu machen, muss diese möglichst in der ersten aktiven Shell gesetzt werden (da alle Programme/Shells Nachfahren der Login-Shell sind). Um den Windowmanager zu ändern und das HOME-Verzeichnis in den Suchpfad aufzunehmen, sind solche Variablen zu exportieren:

export WINDOWMANAGER=/usr/X11R6/bin/fvwm2

export PATH=$PATH:$HOME

Möchte man solche Einstellungen für alle zukünftigen Sitzungen geltend machen, trägt man obige Zeilen in eine Datei .profile in seinem Home-Verzeichnis ein.

7.9.5.2 Lokale Shell-Variablen

Für die jeweils aktive Shell lassen sich lokale Variablen vereinbaren. Solche Variablen sind einzig in dieser Shell und in den darin ausgeführten Programmen sichtbar:

var=10

echo $var
10

string="abc efg"
echo $string
abc efg

bash
echo $var
   
exit
echo $var
10

Lokale wie auch globale Shell-Variablen werden mittels unset <variable>gelöscht.

7.9.5.3 Vordefinierte Variablen

Beim Systemstart und beim Aufruf der Dateien /etc/profile (System-Voreinstellungen) und .profile (benutzereigene Voreinstellungen), die ja auch Shellskripts sind, werden bereits einige Variablen definiert.

Alle aktuell definierten Variablen können durch das Kommando set aufgelistet werden. Einige vordefinierte Variablen sind neben anderen:

Variable Bedeutung
HOME Home-Directory (absoluter Pfad)
PATH Suchpfad für Kommandos und Skripts
MANPATH Suchpfad für die Manual-Seiten
MAIL Mail-Verzeichnis
SHELL Name der Shell
LOGNAMEUSER Login-Name des Benutzers
PS1 System-Prompt ($ oder #)
PS2 Prompt für Anforderung weiterer Eingaben (>)
IFS (internal field separator) Trennzeichen, meist CR, Leerzeichen und Tab)
TZ Zeitzone (z. B. MEZ)

Es gibt eine Reihe von vordefinierten Variablen, deren Benutzung ein wesentlicher Bestandteil des Shell-Programmierens ist. Die wichtigsten eingebauten Shell-Variablen sind:

$! Prozessnummer des letzten Hintergrundprozesses kill -9 $! (Kindermord)
$# Anzahl der Aufrufparameter
$$ Prozessnummer der aktiven Shell kill -9 $$ (Selbstmord)
$* Alle Aufrufparameter
$? Rückgabewert des letzten Kommandos cat /etc/passwd ; echo $?
$@ Alle Aufrufparameter
$0 Name des ausgeführten Shellskripts
$1 1. Element einer Liste
$9 9. Element einer Liste
$n Aufrufparameter mit der Nummer n, n <= 9
$- gesetzte Shell-Optionen echo $-
ERRNO Fehlernummer des letzten fehlgeschlagenen Systemaufrufs
OLDPWD Vorheriges Verzeichnis (wird durch cd gesetzt)
PWD Aktuelles Verzeichnis (wird durch cd gesetzt)

Beispiele

echo $0

-bash

set {a,b,c}{1,2}
echo $*
a1 a2 b1 b2 c1 c2

echo $#
6

echo $4
b2

7.10 Bedingte Ausführung

Die auch als Flusskontrolle bekannten Mechanismen ermöglichen eine kontrollierte Beeinflussung des Programmablaufs. Die Bash stellt die if...fi und case...esac -Konstrukte zur Verfügung.

Erstere Form wird meist zur Unterscheidung einiger weniger Fälle (meist 2) verwendet. Die Syntax lautet:

if Liste von Kommandos; then

   Liste von Kommandos
[elif Liste von Kommandos; then
   Liste von Kommandos]
[else
   Liste von Kommandos]
fi

Von den angegebenen Zweigen werden die Kommandos höchstens eines Zweiges ausgeführt. Entweder die des ersten Zweiges, dessen Bedingung erfüllt ist oder der optionale "else"-Zweig, falls keine Bedingung erfüllt wurde.

Die Bedingung selbst ist der Rückgabewert der Liste der Kommandos (meist also der Rückgabewert des letzten Kommandos der Liste).

Das "case"-Konstrukt wird bei einer höheren Anzahl an Auswahlkriterien bevorzugt. Prinzipiell kann mittels "case" jede "if"-Bedingung abgebildet werden.

Ein wesentlicher Unterschied ist die mögliche Abarbeitung mehrerer Fälle, da alle Anweisungen ab der ersten zutreffenden Bedingung bis zu einem expliziten Verlassen des Konstrukts ausgeführt werden (d.h. ist eine Bedingung erfüllt, werden die nachfolgenden ignoriert).

case Bedingung in

   Muster [ | Muster ])
      Liste von Kommandos
      [;;]
  [Muster [ | Muster ])
      Liste von Kommandos
      [;;]]
esac

Die Bedingung muss ein Token sein. Die Muster unterliegen denselben Expansionen wie Pfadnamen und dürfen somit Metazeichen enthalten.

Stimmt ein Muster mit der Bedingung überein, werden alle nachfolgenden Kommandos bis zum Verlassen des Konstrukts mittels ";;" oder bis zum abschließenden "esac" ausgeführt.

Der typische Anwendungsbereich für "if"- und "case"-Konstrukte ist die Shellprogrammierung und in diesem Zusammenhang werden Ihnen noch genügend Beispiele zur Benutzung begegnen.

if test $(id | awk -F'[=(]' '{print $2}';) -eq "0"; then echo Superuser; else echo Normaler User; fi

Normaler User
su -
Password:
root@lincln01> if test $(id | awk -F'[=(]' '{print $2}';) -eq "0"; then echo Superuser; else echo Normaler User; fi
Superuser

Das (zugegeben... etwas konstruierte) Beispiel entscheidet, ob der aufrufende Benutzer als Root oder als "normaler" Nutzer arbeitet.

Die Verwendung des builtin-Kommandos test ist typisch für Bedingungen.

7.11 Schleifen

Die Bash bietet vier Typen der wiederholten Kommandoausführung. Die "for"-Schleife wird verwendet, um eine Kommandosequenz n-mal auszuführen, wobei die Anzahl vorab fest steht.

Im Unterschied dazu wiederholt die "while"-Schleife die Liste der Kommandos nur so oft, solange die angegebene Bedingung erfüllt ist. "until"-Schleifen sind genau genommen nichts anderes als "while"-Schleifen, wobei die Bedingung negiert wurde, d.h. sie wird solange durchlaufen, bis die Bedingung wahr wird. Schließlich helfen die "select"-Schleifen beim Erzeugen von Auswahlmenüs für Benutzereingaben.

Innerhalb jeder Schleife kann diese durch den Aufruf von break verlassen werden. Ein enthaltenes continue veranlasst das Überspringen nachfolgender Befehle und Fortfahren mit dem nächsten Schleifendurchlauf.

for Variable [ in Wortliste ]; do

    Liste von Kommandos
done

Die for-Schleife wird genau so oft durchlaufen, wie Einträge in der Wortliste stehen. Im ersten Durchlauf wird der erste Eintrag der Wortliste an Variable zugewiesen, im zweiten der zweite Eintrag usw.:

for i in a b c; do echo $i; done

a
b
c

Die Wortliste wird zunächst expandiert (dieselben Mechanismen wie bei einfachen Kommandos). Fehlt die Wortliste, so wird die Liste der Positionsparameter verwendet (innerhalb von Shellskripten ist dies die Liste der Kommandozeilenargumente; auf der Kommandozeile selbst ist es eine vom Kommando set erzeugte Liste):

set a b c

for i; do echo $i; done
a
b
c

Mit Bash-Version 2.0.4 wurde die for- -Schleife um eine an die Programmiersprache C angelehnte Syntaxvariante erweitert:

for ((Ausdruck_1; Ausdruck_2; Ausdruck_3)); do...done

Bei den Ausdrücken handelt es sich um arithmische Substitutionen.* Ausdruck_1 wird üblicherweise die Zuweisung eines Anfangswertes an eine Schleifenvariable beinhalten;

  • Ausdruck_2 dient als Abbruchbedingung und
  • Ausdruck_3 zum Weiterschalten der Schleifenvariablen.

Ausdruck_1 wird nur einmal beim Eintritt in die Schleife ausgewertet; Ausdruck_2 wird vor jedem und Ausdruck_3 nach jedem Schleifendurchlauf neu berechnet.

for ((i=0; i<10; i++)); do echo -n "$i "; done

0 1 2 3 4 5 6 7 8 9

Den Einsatz dieser Form der for-Schleife sollten Sie nur in Betracht ziehen, wenn das Skript nicht portable sein muss oder sicher gestellt ist, dass auf jeder Zielmaschine die Bash in der aktuellsten Version installiert ist.

7.11.1 Beispiel 1

Beim Übersetzen von Softwarepaketen bricht der Vorgang häufig mit einer Fehlermeldung wie "_itoa.o(.text+0x50): undefined reference to `__umoddi3`" ab.

Ursache ist die Verwendung von "__umoddi3". Vermutlich wurde vergessen, eine bestimmte Bibliothek, die das Symbol enthält, hinzuzulinken.

Nun gilt es herauszufinden, welche Bibliothek notwendig ist. Die folgende Kommandozeile findet diese, sofern sie existiert:

for i in $(find /usr -name "*.a" 2>/dev/null); do nm $i 2>/dev/null | grep -sq "T __umoddi3" && echo $i; done

/usr/lib/gcc-lib/i486-linux/egcs-2.91.66/libgcc.a
/usr/lib/gcc-lib/i486-linux/2.7.2.3/libgcc.a
/usr/i486-glibc20-linux/lib/gcc-lib/i486-glibc20- linux/egcs-2.91.66/libgcc.a

Das Beispiel beschränkt die Suche auf statische Bibliotheken unterhalb von "/usr". nm liest aus jeder Bibliothek die enthaltenen Symbole ("nm" vermag auch die Symbole von Object-Dateien lesen), mit grep suchen wir nach dem Symbol.

Uns interessieren allerdings nur Bibliotheken, wo das Symbol definiert ist (und nicht solche, die es nur verwenden), deshalb die Suche nach "T __umoddi3" (Rufen Sie mal "nm /usr/lib/libc.a" auf und durchforsten die Ausgabe, um den Zweck zu verstehen).

7.11.2 Beispiel 2

Der Windows-Anwender mag die Möglichkeit vermissen, mehrere Dateien mit einer bestimmten Dateikennung zu kopieren und die Dateikennung gleichzeitig zu verändern ("copy *.bat *.bak").

Hat der Leser die Mechanismen einer Unix-Shell verstanden, sollte ihm geläufig sein, warum ein solcher Aufruf unter Unix nicht das erwartete Resultat erzielt.

Mit Hilfe einer "for-Schleife" lässt sich das COMMAND.COM-Verhalten einfach simulieren:

for i in $(ls *.bat); do cp $i ${i%.*}.bak; done

Die Generierung des Ziel-Dateinamens enthält eine Parametersubstitution.

7.11.3 while und until-Schleife

Die while-Schleife evaluiert die ihr unmittelbar folgenden Kommandos (zwischen "while" und "; do") und führt die Kommandos des Schleifenkörpers solange aus, wie der Rückgabestatus des letzten Kommandos der Liste gleich Null ist.

Die until-Schleife quittiert dagegen ihren Dienst, sobald das letzte Kommando der Liste den Wert Null zurück liefert:

while Bedingung do

    Liste von Kommandos
done

until Bedingung do
    Liste von Kommandos
done
7.11.3.1 Beispiel 1

Die nachfolgende Schleife berechnet die Werte 2n für n=1..8:

declare -i i=1; z=2; while [ $i -le 8 ]; do echo "2^$i=$z"; i=i+1; z=$((2*$z)); done

2^1=2
2^2=4
2^3=8
2^4=16
2^5=32
2^6=64
2^7=128
2^8=256
7.11.3.2 Beispiel 2

Die folgende while-Schleife zählt, wie viele Benutzer die Gruppe "100" als default-Gruppe besitzen:

(exec < /etc/passwd; IFS=":"; declare -i users=0; while read line; do set $line; test $4 -eq "100" && users=users+1 ; done; echo "Gruppe users hat $users Default-Mitglieder")

Da die Standardeingabe mittels "exec" auf die Datei /etc/passwd umgeleitet wurde, ist die letzte Eingabe, die aus dieser Quelle kommt, das EndOfFile (EOF).

Die Bash unter Linux ist allerdings (meist) so konfiguriert, dass EOF auch zum Beenden der Shell führt ([Strg]+ [D]). Um die aktuelle Shell nicht unbeabsichtigt ins Nirwana zu delegieren, wurde die Ausführung in einer Subshell vorgenommen.

Der Internal Field Separator (IFS) beinhaltet die Trennzeichen, anhand derer die Bash die Eingaben in einzelne Worte zerlegt. Normalerweise sind diese die Whitespaces; wir benötigen aber den Doppelpunkt, da jener die Felder der Passwortdatei aufteilt.

Innerhalb der "while-Schleife" lesen wir jeweils eine Zeile in "line" ein (mittels read) und erzeugen eine Parameterliste (set $line).

In dieser interessiert der vierte Parameter (Gruppeneintrag). Ist dieser "100", wird die Variable "users" hoch gezählt.

Näheres zu "IFS", "read" und "set" im Abschnitt Initialisierungen.

Die Komplexität obiger Anwendungen von "while" zeigt auf, dass die Verwendung von Schleifen weniger auf der Kommandozeile verbreitet ist, sondern zu wichtigen Bestandteilen in Shellskripten zählt.

7.11.4 select

select hat mit dem klassischen Schleifenkonzept nichts gemein; sie schreibt in einer Endlosschleife eine Liste von Auswahlalternativen auf die Standardfehlerausgabe und wartet auf eine Interaktion mit dem Benutzer.

select Name [ in Liste ] do

    Liste von Kommandos
done

Das Prinzip lässt sich am einfachsten anhand eines Beispiels demonstrieren. In einem Menü sollen die Dateien des aktuellen Verzeichnisses präsentiert werden.

Zur vom Benutzer ausgewählten Datei werden weitere Informationen ausgegeben; außerdem soll die Beendigung der Schleife angeboten werden:

select file in * Ende; do

> if test -e $file; then
> ls -l $file;
> else
> break;
> fi;
>done

1)./index.html      5)./help.txt       9)./symbol9.html  13)./pfad.gif
2)./symbol6.html    6)./symbol5.html  10)./symbol2.html  14) Ende
3)./foo.tgz         7)./symbol3.html  11)./symbol4.html
4)./symbol8.html    8)./symbol7.html  12)./symbol1.html
#? 3
-rw-r--r--   1   user   users       3102 Aug 17 10:49 foo.tgz
1)./index.html      5)./help.txt       9)./symbol9.html  13)./pfad.gif
2)./symbol6.html    6)./symbol5.html  10)./symbol2.html  14) Ende
3)./foo.tgz         7)./symbol3.html  11)./symbol4.html
4)./symbol8.html    8)./symbol7.html  12)./symbol1.html
#? 14

Die Liste in "select" wird allen Expansionen (wie bei einfachen Kommandos) unterzogen. Diese Liste wird allerdings nur beim ersten Eintritt in die Schleife generiert. Eine Änderung dieser wird also im Menü nicht sichtbar.

7.11.5 Verschachtelte Schleifen

Angenommen, das letzte Beispiel sollte dahin gehend modifiziert werden, dass die erwählte Datei gelöscht werden soll. Das Problem ist nun, dass diese

Datei im nachfolgenden Menüaufrufs noch immer gelistet wird. Eine Lösung wäre die Kapselung des select-Aufrufs in einer umgebenden "while-Schleife".

Die "select"-Auswahl wird nun nach jedem Durchlauf verlassen, sodass das Menü erneut aufgebaut wird. Um nun beide Schleifen zu verlassen, ist die zu verlassenden Schleifentiefe dem "break"-Aufruf mitzugeben:

while :; do

> select file in * Ende; do
> if test -e $file; then
> rm $file;
> break;
> else
> break 2;
> fi;
> done
> done
1)./index.html      5)./help.txt       9)./symbol9.html  13)./pfad.gif
2)./symbol6.html    6)./symbol5.html  10)./symbol2.html  14) Ende
3)./foo.tgz         7)./symbol3.html  11)./symbol4.html
4)./symbol8.html    8)./symbol7.html  12)./symbol1.html
#? 3
1)./index.html      5)./symbol5.html   9)./symbol2.html  13) Ende
2)./symbol6.html    6)./symbol3.html  10)./symbol4.html
3)./symbol8.html    7)./symbol7.html  11)./symbol1.html
4)./symbol8.html    8)./symbol9.html  12)./pfad.gif
#? 13

8 Eingebaute Kommandos

Es liegt wohl in der Natur des Linux-Neulings, seine Testprogramme und -skripten «"test« zu benennen (eigentlich kann ich mich an keinen Linuxkurs erinnern, indem nicht mindestens einer der Teilnehmer auf diese Weise mit den builtin Kommandos der Bash konfrontiert wurde).

Nach verrichteter Arbeit zeigte der Testlauf:

ls -l test

-rwxr-xr-x  1   user    users       12177 Sep 23 10:52 test
test

Mit Kenntnis des Vorgehens der Bash bei der Suche nach einem Kommando, gelangt man bald zum Schluss, dass sich ein builtin Kommando vorgedrängelt hat.

Es gibt eine Fülle solcher eingebauter Kommandos und mindestens 4 Gründe, warum solche in der Bash überhaupt existieren: # Weil es ein solches Kommando in Unix nicht gibt (Beispiel "source")

  1. Weil ein builtin Kommando effizienter arbeitet, als ein externes Kommando (keine Prozesserzeugung notwendig; Beispiel »echo«)
  2. Weil nur ein eingebautes Kommando Bash-interne Variablen ändern kann (Beispiel »export«)
  3. Weil ein Kommando wie »exec« nur innerhalb der Bash realisierbar ist

Betrachten wir die einzelnen builtin-Kommandos:

Etliche der besprochenen Shell-Kommandos starten nicht, wie sonst üblich, einen eigenen Prozeß, sondern sie werden direkt von der Shell interpretiert und ausgeführt.

Teilweise ist keine E/A-Umleitung möglich. Etliche Kommandos der folgenden Auswahl wurden schon besprochen.

Andere werden weiter unten behandelt. Zum Teil gibt es interne und externe Versionen, z. B. 'echo' (intern) und '/bin/echo' (extern).

break Schleife verlassen
continue Sprung zum Schleifenanfang
echo Ausgabe
eval Mehrstufige Ersetzung
exec Überlagerung der Shell durch ein Kommando
exit Shell beenden
export Variablen für Subshells bekannt machen
read Einlesen einer Variablen
shift Parameterliste verschieben
trap Behandlung von Signalen

8.1 Null - Kommando :

Dieses »Kommando« tut nichts, außer einen Rückgabewert »0« zu erzeugen (»0« ist der übliche Rückgabewert eines Kommandos unter Unix, wenn seine Ausführung erfolgreich war).

Nützlich ist es in Shellskripten, falls Sie in Bedingungen einen wahren Wert (»true«) benötigen oder an Positionen, wo syntaktisch ein Kommando erwartet wird, Sie aber keines benötigen:

while : ;do echo "Eine Endlosschleife"; done

Eine Endlosschleife
Eine Endlosschleife
Eine Endlosschleife
...
Eine Endlosschleife[Strg]+[C]
if test -a foo ; then :; else echo "Datei nicht existent"; fi
Datei nicht existent

8.2 source -Kommando .

Die angegebene Datei wird gelesen und innerhalb der Shellumgebung ausgeführt.

Ist die Datei ohne Pfad angegeben, wird dabei in Verzeichnissen der PATH-Variable gesucht.

Wird sie dort nicht gefunden, wird das aktuelle Verzeichnis betrachtet. Das Kommando kann sich der C-Programmierer als "include" der Bash vorstellen.

Manche Linuxdistributionen (bspw. RedHat) verwenden dieses Kommando in ihren Runlevel-Skripten, um eine Funktionsbibliothek einzubinden.

Auf der Kommandozeile bietet sich "source" an, um die initiale Umgebung zu rekonstruieren (weil man u.U. die Umgebungsvariablen "verbogen" hat):

source /etc/profile

# bzw.
dirkw@lincln01>. /etc/profile

8.3 alias

Dient der Definition einer Abkürzung für ein(e) Kommando(folge). Mit der Option -p werden alle vorhandenen Aliasse aufgelistet; Beispiele wurden bereits im einleitenden Abschnitt zu Bash (Eingabehilfen) und in Allgemeines zu Shells genannt.

8.4 jobs

Listet die Jobnummern aller Hintergrundprozesse auf.

8.5 bind

Das Kommando besitzt in erster Linie im Zusammenspiel mit der Datei /etc/inputrc (bzw. ~/.inputrc) eine Rolle. Besagte Dateien enthalten die »Keybindings«, also die Zuordnungen von Tastenkombinationen zu bestimmten Funktionalitäten.

Wenn bspw. die unter Interaktive Bash aufgeführten Eingabehilfen bei Ihnen nicht funktionieren sollten, dann fehlt in der Konfiguration Ihrer Bash die notwendige Zuordnung (oder Sie verwenden eine ältere Bashversion).

bind kann auch interaktiv benutzt werden, um bestimmte Keybindings zu löschen, um sie sich anzeigen zu lassen oder neue zu definieren.

Letzteres ermöglicht gar die Ausführung von Kommandos bei Betätigung einer zuvor definierten Tastensequenz. Benötigen Sie solche Sequenzen häufig, so nehmen Sie sie in ihre persönliche ~/.inputrc auf.

Wenn Sie mit der Bash einigermaßen per Du sind, so kennen

Sie die eine oder andere Tastenkombination, um geschwind auf der Kommandozeile zu navigieren oder diese zu manipulieren. Hinter den Mechanismen verbergen sich readline-Funktionen.

Welche es gibt, verrät der Aufruf »bind -l« und über deren aktuelle Belegung weiß »bind -p« bescheid. Erscheint in letzter Ausgabe »not bound«, so ist diese Funktion nicht belegt.

bind -p | head -5

"\C-g": abort
"\C-x\C-g": abort
"\e\C-g": abort
"\C-j": accept-line

Angenommen Sie verfassen im vi ein deutschsprachiges html-Dokument.

Um die Zeichen »ä«, »ö«,... in Browsern, die deutsche Zeichensätze nicht unterstützen, korrekt darzustellen, sollten alle länderspezifischen Zeichen im Unicode dargestellt werden, also bspw. »ü« anstatt »ü«.

Die komfortable Lösung ist das Verändern der Tastaturbelegung, so dass automatisch der Unicode im Text erscheint. In der Bash erreichen Sie dies wie folgt:

bind '"ü":"ü"'

Sobald Sie »ü« tippen, erscheint in der Eingabe »ü«. Um die Bindung aufzuheben, geben sie »bind -r <Tastensequenz>« an; aber für gewöhnlich besteht der Wunsch, die alte Belegung wieder herzustellen.

Eine Taste, die »als sie selbst« definiert wird, wird in folgender Manier belegt:

bind 'ü:self-insert'

Obige Form der Tastenbindung nennt man auch Makro; daneben können sie auch Funktionen und gar Kommando(s) an Tasten(sequenzen) binden.

Das nachfolgende Beispiel bindet [Strg]+[b] an die Ausgabe des Kalenders des aktuellen Monats:

bind -x '"\C-b":cal'

[Strg]+[B]
    November 2000
So Mo Di Mi Do Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

Ihre gesammelten Werke der Tastenbindungen können Sie auch in eine beliebige Datei schreiben und diese mittels "bind -f <Datei>" laden.

8.6 break [n]

Dient zum expliziten Verlassen einer Schleife.

Ohne Angabe eines Arguments wird die unmittelbar umgebende Schleife verlassen; möchte man tiefere Verschachtelungen verlassen, muss die Tiefe angegeben werden:

...

while [ Bedingung ]; do
   for i in Liste; do
      case "$i" in
        foo*) break;
        bla*) tue etwas ;;
        *) Fehler; break 2;
      esac
   done
done
...

8.7 break

Die Syntax der break-Anweisung lautet wie folgt:

break [ n ]

Mit break kann man die innerste Ebene (bzw. n Schleifenebenen) verlassen ohne den Rest der Schleife auszuführen.

8.8 builtin Kommando

Bei der Suche nach Kommandos betrachtet die Shell Aliasse und Funktionen noch vor den eingebauten Kommandos. Überdeckt nun ein solcher Name ein builtin-Kommando, so wird beim einfachen Aufruf immer der Alias bzw. die Funktion ausgeführt werden.

Mit vorangestelltem builtin weist man nun die Bash an, auf jeden Fall ihr eingebautes Kommando aufzurufen.

Der Rückgabestatus ist gleich dem Rückgabewert des Builtin's oder "falsch", falls das Kommando kein builtin ist.

8.9 cd

Wechsel des Verzeichnisses

8.10 command

Ein weiterer Weg, um die Verwendung von Aliassen oder Funktionen bei der Suche nach einem Kommando temporär auszuschalten, ist ein dem zu startenden Kommandonamen voranzustellendes command.

Die Shell sucht nun einzig in der Liste der eingebauten Kommandos und den Pfaden aus PATH.

Hilfreich ist die Option -p, falls die PATH-Variable einmal "völlig daneben" belegt ist; die Bash sucht in default-Pfaden und findet so zumindest die wichtigsten Programme.

8.11 compgen

Mit dem Kommando lassen sich die möglichen Expansionen anzeigen. Um bspw. gezielt die denkbaren Erweiterungen aller mit "l" beginnenden Aliasse zu erhalten, ist folgende Kommandozeile notwendig:

compgen -A alias l

l
la
ll
ls
ls-l

Anstatt von "alias" können u.a. Funktionen ("function"), Schlüsselworte der Bash ("keywords"), Dateinamen ("file"), Verzeichnisse ("directory"), Variablen ("variable") expandiert werden. Noch weit reichender sind die Möglichkeiten in Bezug auf Dateinamen, da hier mit Suchmustern gearbeitet werden kann:

compgen -G '*c*'

Packages
bla.xcf
countdir
linuxbuch
Documents

Abgesehen von -r und -r verwendet compgen dieselben Optionen wie das nachfolgend beschriebene complete.

8.12 complete

Mit diesem Kommando kann das ganze Verhalten der Bash bei der Vervollständigung von Argumenten verändert werden.

Bevor wir uns mit den Optionen auseinander setzen, soll ein Beispiel die Mächtigkeit des Konzepts andeuten.

Beispiel: Als Argumente für das Programm xv (dient der Anzeige von Dateien diverser Grafikformate) sollen bei der automatischen Dateinamensergänzung nur Dateien mit einer typischen Dateiendung (*.jpg, *.gif,...) berücksichtigt werden. Mit complete müsste die Bash wie folgt eingerichtet werden:

  1. Die Option "extglob" (vergleiche shopt) muss gesetzt sein:
shopt -q extglob || shopt -s extglob 

# Test des bisherigen Verhaltens:
xv s[Tab][Tab]
sax_0.gif     sax_norm.tmp  sax_xxx.jpg
sax_grob.tmp  sax_post.tmp  start_taper

complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' xv
# Test des jetzigen Verhaltens:
xv s[Tab][Tab]
sax_0.gif  sax_xxx.jpg

Mit der Option -p lässt sich jede Spezifikation anzeigen und mit -r auch wieder löschen. Wird kein Name angegeben, betrifft die Aktion jede Spezifikation:

complete -p

complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' xv
complete -r xv

complete wurde im Beispiel durch die Option -f angewiesen, den folgenden Ausdruck einer Dateinamenserweiterung zu unterziehen. Alternative Expansionen sind (Auswahl):

-a Es wird eine Expansion zu einem bekannten Aliasnamen versucht
-b Erweiterung zu einem eingebauten Kommando
-c Erweiterung zu einem Kommando
-e Exportierte Variable

Als Beispiel definieren wir eine Funktion, die den Inhalt einer exportierten Variablen anzeigen soll:

show_exports() { echo $*; }

Beim Aufruf dieser Funktion sollen bei versuchter Expansion alle möglichen Erweiterungen angezeigt werden. Um sich den Inhalt der Variablen selbst anzuschauen, muss dieser ein Dollarzeichen voranstehen. Mit der Option -P lässt sich ein solches Präfix für ein Expansionsergebnis fest legen. Die complete-Zeile sollte wie folgt aussehen:

complete -e -P '$'

# Test
show_exports PO[Tab]
show_exports $POVRAYOPT
-l/usr/lib/povray/include
-f Dateinamenserweiterung
-u Benutzernamen

Als Beispiel soll das Kommando mail mit einem Benutzernamen expandiert werden. Als Suffix wird automatisch "@outside.all" ergänzt:

complete -u -S '@outside.all' mail

# Test
mail a[Tab]
mail alf@outside.all
-v Variablennamen

Als weitere Optionen stehen zur Verfügung:

-G Muster Es wird eine Dateinamenserweiterung mit dem Muster versucht
-C Kommando Das Kommando wird in einer Subshell gestartet und dessen Ausgabe als mögliche Vervollständigung eingesetzt
-X Muster Es wird eine Dateinamenserweiterung nach Ausführung aller anderen Optionen versucht. Alle Dateinamen, die mit dem Muster übereinstimmen, werden aus der Liste der möglichen Vervollständigungen entfernt
-P Präfix Nach Ausführung aller anderen Optionen werden die möglichen Erweiterungen mit dem angegebenen Präfix versehen
-S Suffix Nach Ausführung aller anderen Optionen werden die möglichen Erweiterungen mit dem angegebenen Suffix versehen

8.13 continue

Die Syntax der continue-Anweisung lautet wie folgt:

continue [ n ]

Man benutzt continue um die restlichen Befehle in einer Schleife zu überspringen und mit dem nächsten Schleifendurchlauf anzufangen.

Wenn der Parameter n angegeben wird, werden n Schleifenebenen übersprungen.

continue [n]

Dieses Kommando kann innerhalb von Schleifen verwendet werden, um unmittelbar mit dem nächsten Schleifendurchlauf fortzufahren.

Ohne Angabe von "n" ist die umgebende Schleife gemeint; mit Angabe einer ganzen Zahl "n" kann im Falle von verschachtelten Schleifen diejenige benannt werden, die zu durchlaufen ist

for i in $(ls); do

   test -d $i && continue
   # tue etwas...
done

8.14 declare

Zum Deklarieren von Variablen, wobei diese gleichzeitig mit Attributen versehen werden können; siehe unter Variablen

8.15 dirs

Dient zur Anzeige des Verzeichnisstacks. in Verzeichnis kann neben dem bekannten Kommando cd auch mit dem eingebauten Kommandos pushd bzw. popd gewechselt werden.

Bei letzterem Vorgehen wird das Verzeichnis auf einem Stack abgelegt, dessen Einträge popd in umgekehrter Reihenfolge entfernt und in das jeweilige Ausgangsverzeichnis wechselt.

8.16 disown [Jobnummer(n)]

Das Kommando ist die bash-eigene Realisierung des Kommandos nohup und ermöglicht, Jobs nachträglich vom Elternprozess (also die Shell) zu lösen.

D.h. bei Beendigung der Shell erhalten diese Prozesse kein Signal SIGHUP, sodass sie weiterhin existieren. Gleichzeitig werden die Jobs aus der Jobtabelle entfernt, sodass sie bspw. vom Kommando jobs nicht mehr berücksichtigt werden können.

Die Option -h schützt einen Prozess vor SIGHUP ohne ihn aus der Jobtabelle zu verbannen. Mit -a kann disown auf alle Jobs und mit -r auf alle laufenden (Status "Running") Jobs ausgedehnt werden.

8.17 echo - Eine Zeile Text anzeigen

echo [OPTION]... [ZEICHENKETTE]...

Gib ZEICHENKETTE(n) auf Standardausgabe aus.

Optionen

-n gib keinen Zeilenvorschub am Ende der Zeile aus
-e ermögliche Interpretation der \-Notationen unten
-E verhindere Interpolation einiger Sequenzen in ZKETTEn
--help gib diese Hilfe aus und beende das Programm
--version gib Versionsinformation aus und beende das Programm

Ohne -E werden die folgenden Sequenzen erkannt und berechnet

\NNN Zeichen mit dem ASCII Code (oktal)
\\ Rückschrägstrich
\a Alarm (BEL)
\b Zeichen zurück
\c unterdrücke Zeilenvorschub am Ende
\f Seitenvorschub
\n Zeilenvorschub
\r Wagenrücklauf
\t horizontaler Tabulatorstopp
\v vertikaler Tabulatorstopp

echo

Gibt alle Argumente, getrennt durch ein Leerzeichen, und einen abschließenden Zeilenumbruch aus.

Die Option -n unterdrückt den Zeilenumbruch und -e erzwingt die Auswertung Escape-Sequenzen:

echo "\a"

\a
echo -n "\a"
\aecho -e "\a"
PIEP

Das nächste Beispiel verwendet "\b", um den Cursor um eine Position zurück zu bewegen:

cat ticker

#!/bin/sh

declare -i zeit=0
echo -en "Sekunden: \t"

while :; do
   for ((i=${#zeit}; i; i--)); do
      echo -en "\b"
   done
   echo -en $zeit
   zeit=zeit+1
   sleep 1
done

8.18 enable

Die eingebauten Kommandos der Shell lassen sich bei Bedarf aktivieren oder auch abschalten.

Letzteres kann nützlich sein, wenn Sie fortwährend auf ein externes Kommando zugreifen müssen, das denselben Namen wie ein Shell-Builtin besitzt.

Da die Shell immer ihr eigenes Kommando bevorzugt, müssten Sie ständig den vollständigen Pfad eintippen - auf die Dauer ein lästiges Unterfangen.

Mit -n <Kommando> deaktivieren Sie das Builtin; ohne Eingabe einer Option lässt es sich wieder aktivieren.

8.19 eval

Das Kommando eval liest seine Argumente, wobei die üblichen Ersetzungen stattfinden, und führt die resultierende Zeichenkette als Kommando aus.

Die Argumente der Kommandozeile werden von der Shell gelesen, wobei Variablen- und Kommandoersetzungen sowie Dateinamenersetzung durchgeführt werden.

Die sich ergebende Zeichenkette wird anschließend erneut von der Shell gelesen, wobei wiederum die oben genannten Ersetzungen durchgeführt werden. Schließlich wird das resultierende Kommando ausgeführt. Beispiel (Ausgaben fett):

$ A="Hello world!"

$ X='$A'
$ echo $X
$A
$ eval echo $X
Hello world!

Der Rückgabestatus von eval ist der Rückgabestatus des ausgeführten Kommandos oder 0, wenn keine Argumente angegeben wurden. Ein weiteres Beispiel:

$ cat /etc/passwd | wc -l

76
$ foo='cat /etc/passwd'
$ bar=`| wc -l'
$ $foo $bar # Fehler: $bar ist Argument von cmdl
cat: | : No such file or directory
cat: wc: No such file or directory
cat: -l: No such file or directory
$ eval $foo $bar
76

In diesem Beispiel wird zunächst ein einfaches Kommando gestartet, das die Anzahl der Zeilen der Datei /etc/passwd bestimmt.

Anschließend werden die beiden Teile des gesamten Kommandos in die zwei Shell-Variablen foo und bar aufgeteilt.

Der erste Aufrufversuch $foo $bar bringt nicht das gewünschte Ergebnis, sondern lediglich einige Fehlermeldungen, da in diesem Fall der Wert von bar als Argument für foo interpretiert wird ('cat' wird mit den Dateien '/etc/passwd', '|', 'wc' und '-l' aufgerufen).

Wird jedoch das Kommando eval auf die Argumente $foo und $bar angewendet, werden diese zunächst zur Zeichenkette "cat /etc/passwd | wc -l" ersetzt.

Diese Zeichenkette wird dann durch das Kommando eval erneut von der Shell gelesen, die jetzt das Zeichen "|" in der Kommandozeile als Pipesymbol erkennt und das Kommando ausführt.

Das Kommando eval wird üblicherweise dazu eingesetzt, eine Zeichenkette als Kommando zu interpretieren, wobei zweifach Ersetzungen in den Argumenten der Kommandozeile vorgenommen werden.

Eine andere Anwendung ist beispielsweise die Auswahl des letzen Parameters der Kommandozeile.

Mit \$$# erhält man die Parameterangabe (bei fünf Parametern --> $5).

Das erste Dollarzeichen wird von der Shell ignoriert (wegen des '\'), $# hingegen ausgewertet. Durch eval wird der Ausdruck nochmals ausgewertet, man erhält so den Wert des letzten Parameters:

eval \$$#

Aber Vorsicht, das klappt nur bei 1 - 9 Parametern, denn z. B. der 12. Parameter führt zu $12 --> ${1}2. Es lassen sich mit eval sogar Pointer realisieren.

Falls die Variable PTR den Namen einer anderen Variablen, z. B. XYZ, enthält, kann auf den Wert von XYZ durch eval $PTR zurückgegriffen werden, z. B. durch eval echo \$$PTR.

eval

Die Expansionen der Bash laufen nach wohldefinierten Regeln ab. Wurden sie der Reihe nach behandelt, wird die resultierende Kommandozeile ausgeführt. Manchmal enthält diese Kommandozeile wiederum Bestandteile, die man einer Expansion gern unterziehen würde. Genau hierzu verhilft eval:

var=foo

bla='$var' 
echo $bla
$var
eval echo $bla
foo

8.20 exec

Das Kommando besitzt zwei Bedeutungen.

Wird ihm als Argument ein Kommandoname mitgegeben, so wird das aktuell ausgeführte Programm (also die Shell bzw. ein Shellskript) durch dieses neue Programm ersetzt. Das hat zur Folge, dass mit Beendigung des Kommandos auch die Shell nicht mehr existiert.

Diese Art der Anwendung von exec hat seinen Ursprung vermutlich in Zeiten begrenzter Hardwareressourcen. So "optimierte" man Shellskripte dahingehend, dass der letzte Befehl per exec gestartet wurde.

Dies ersparte eine weitere Prozesserzeugung und verlief somit etwas schneller, als es mit einem neuen Prozess der Fall gewesen wäre (und es sparte den (Speicher)Overhead für den Prozess).

Nützlich mag das Kommando in dem Sinne höchstens zum Ersetzen der aktuellen Shell durch eine andere sein:

echo $SHELL

/bin/bash
exec tcsh
/home/user>

Der zweite - und wichtigere - Anwendungsbereich für exec ist die Zuordnung eines Dateideskriptors zu einer Datei.

Angenommen, Sie möchten alle Fehlerausgaben eines Shellskripts in eine Datei umleiten.

Mit den bislang bekannten Mitteln könnten sie entweder alle kritschen Kommandos im Shellskript separat umleiten oder Sie könnten alle Kommandos gruppieren und die Fehlerausgabe gemeinsam abfangen.

Einfacher ist es, folgende Zeile an den Anfang des Skripts zu setzen:

exec 2>error.log

Erläuterung: Hiermit wird die Fehlerausgabe (Deskriptor 2) mit der Datei "error.log" verbunden.

Mit Beendigung des Shellskripts geht die Bindung wieder verloren.

Das zweite Beispiel demonstriert das Öffnen einer Datei mit exec, sodass die Eingabe aus dieser bezogen wird. Wir simulieren das Kommando nl, das die Zeilen seiner Eingabe nummeriert:

(typeset -i n=1; exec < testdat;

> while read line; do
> echo "$n $line"; n=n+1;
> done)
1 eins
2 zwei
3 drei
4 vier

Erläuterung: Die Testdatei enthält die Zeilen "eins", "zwei" usw.; die Eingabe wird aus dieser bezogen. Mittels read wird zeilenweise gelesen und jede Zeile mit vorangestellter Nummer ausgegeben.

Das Rechnen mit "n" ist möglich, da die Variable zuvor als Ganzzahl vereinbart wurde. Alles wurde in einer Subshell gestartet, damit die aktuelle Shell nicht beendet wird.

Ein letztes Beispiel soll die Thematik vorerst beenden (die Shellprogrammierung ist ein wesentlich ergiebigeres Anwendungsfeld für exec).

Es demonstriert eine effiziente Möglichkeit, eine konkrete Zeile einer Datei zu manipulieren. Unsere Testdaten seien die Folgenden:

cat testfile

Erste Zeile.
Zweite Zeile.
Zweite Zeile.
Vierte Zeile.

Aus der Datei müssen wir nicht nur lesen, sondern gleichzeitig in diese schreiben können. Wir werden nach der Zeile "Zweite Zeile." suchen und die folgende Zeile durch "Dritte Zeile." ersetzen.

Eine Kommandofolge, die dies realisiert, ist diese:

(exec <>testfile 1>&0

> while read line; do
> echo $line | grep -q "Zweite*" && echo -n "Dritte Zeile."
> done) 

Erläuterung: Die Eingabedatei zum Lesen und Schreiben zu öffnen, würde allein nichts nützen, da hiermit nur der Dateideskriptor (Nummer 0) betroffen ist.

Deshalb muss die Standardausgabe ebenfalls auf diesen Deskriptor umgelenkt werden. Wird die mit "Zweite..." beginnende Zeile gefunden, zeigt der Dateizeiger bereits auf den Beginn der dritten Zeile. Deshalb landet die Ausgabe "echo..." genau dort.

Wenn Sie Zweifel an den Optionen von grep bzw. echo hegen, so testen Sie, was passiert, wenn Sie diese entfernen.

exec

Ähnlich wie beim Dot-Kommando wird keine Subshell erzeugt, sondern die Kommandozeile in der aktuellen Umgebung ausgeführt.

Eine erste Anwendung liegt darin, das aktuelle Programm durch ein anderes zu überlagern. Wenn Sie z. B. die Bourne-Shell als Login-Shell haben, aber lieber mit der C-Shell arbeiten, können sie die Bourne-Shell durch die Kommandozeile

exec /bin/csh

als letzte Zeile in der .profile-Datei durch die C-shell ersetzen (Wenn Sie die C-Shell nur Aufrufen, müssen Sie beide Shells beenden, um sich auszuloggen).

Das Kommando entspricht also dem Systemcall exec(). Wird jedoch kein Kommando angegeben, kann die E/A der aktuellen Shell dauerhaft umgeleitet werden. Beispiel:

exec 2>fehler

leitet alle folgenden Fehlerausgaben in die Datei "fehler" um, bis die Umleitung explizit durch

exec 2>-

zurückgenommen wird. Es können bei exec auch andere Dateideskriptoren verwendet werden. Ebenso kann auch die Dateiumleitung einer Eingabedatei erfolgen, z. B.:

exec 3< datei

Danach kann mit read <&3 von dieser Datei gelesen werden, bis die Umleitung mit exec 3<- wieder zurückgenommen wird.

Man kann also in Shellskripten durch das Einfügen einer exec-Anweisung die Standardausgabe/-eingabe global für das gesamte Skript umleiten, ohne weitere Änderungen vornehmen zu müssen (eine andere Möglichkeit wäre die oben beschriebene Verwendung von { }).

8.21 exit

Wie schon bei der interaktiven Shell kann auch eine Shell-Skript mit exit abgebrochen werden.

Vom Terminal aus kann mit der DEL-Taste abgebrochen werden, sofern das Signal nicht abgefangen wird (siehe trap). Die Syntax der exit-Anweisung lautet wie folgt:

exit [ n ]

Die exit-Anweisung wird benutzt, um ein Skript zu beenden. Wenn der Parameter n angegeben wird, wird er von dem Skript als Exit-Code zurückgegeben.

exit [n]

Beendet die Shell (das Shellskript) mit dem angegebenem Rückgabewert. Wird kein Wert angegeben, liefert exit den Status des letzten Kommandos.

8.22 export

Dient zum exportieren von Variablen und - mit der Option -f von Funktionen, so dass diese ab der (Shell)Prozess ihrer Definition auch in allen abgeleiteten Prozessen sichtbar sind ("globale Variablen").

Mit der Option -n kann die export-Eigenschaft entzogen werden. -p listet alle exportierten Variablen und Symbole auf:

PATH=$PATH:/usr/local/bin

export PATH

8.23 fc

Siehe unter Manipulation der History-Einträge

8.24 fg [Job]

Holt den angegebenen ("Jobnummer") bzw. den zuletzt gestarteten Hintergrundprozess in den Vordergrund.

8.25 getopts OPTIONEN Variable

Jeder Programmierer kennt den Aufwand beim Parsen der an ein Programm übergebenen Argumente und Optionen. Sind die Optionen erlaubt? Stimmt die Reihenfolge? Ist das Argument zulässig?...?

Die meisten Programmiersprachen bringen in ihrem Sprachschatz eine Funktion mit, die eine solche Auswertung wesentlich vereinfacht. Die Funktion der Bash ist »getopts«.

Dieses Kommando erlaubt einem Shellskript die unter Unix übliche Gruppierung von Optionen zu verwenden. Anstatt "-l -a -z" kann auch "-laz" oder "-a -lz"... geschrieben werden.

Solche Angaben über die bereits besprochenen Positionsparameter auszuwerten, ist zwar denkbar, aber mehr als verzwickt.

Im Zusammenhang mit der Bashprogrammierung werden wir »getopts« extensiv verwenden, an dieser Stelle soll ein kleines Beispiel das Gefühl für die Anwendung schulen.

Ein Skript soll die Optionen "-a", "-l", "-F" und "-f <Datei>" verstehen.

Beachten Sie, dass "-f" die Angabe eines Arguments erfordert. Die notwendige Zeichenkette der OPTIONEN ist "alf:F".

Die Reihenfolge der Optionen ist unwichtig, entscheidend ist der Doppelpunkt hinter "-f:", der »getopts« mitteilt, dass dieser Option ein Argument folgen muss:

cat parseline

#!/bin/sh

while getopts alf:F Optionen; do
   case $Optionen in
      a) echo "Option a";;
      l) echo "Option l";;
      f) echo "Option f Argument ist $OPTARG";;
      F) echo "Option F";;
   esac
done

parseline -aF -f
Option a
Option F
./parseline: option requires an argument – f

parseline -f so_gehts -l
Option f Argument ist so_gehts
Option l

Benötigt eine Option ein Argument, kann auf dieses über die Variable OPTARGS zugegriffen werden; OPTIND enthält zu jedem Zeitpunkt den Index auf die nächste von getopts zu betrachtende Option.

In Shellskripten wird man Fehlermeldungen von getopts abfangen. Dies geht entweder über die Umleitung der Standardfehlerausgabe oder durch Setzen der Variable OPTERR auf 0.

8.26 hash

Ein auf der Kommandozeile angegebenes Kommando muss von der Shell gesucht werden.

Die Suche, vor allem wenn sie die Angaben aus PATH einschließt, kostet Zeit. Deswegen merkt sich die Bash die Zugriffspfade zu allen externen Kommandos in einer Hashtabelle.

Diese Hashtabelle wird zuerst konsultiert (es sei denn hashall ist nicht gesetzt; vergleiche set) und nur wenn der Pfad zu einem Kommando dort nicht erfasst wird, werden die Verzeichnispfade betrachtet.

Beim nächsten Zugriff auf dasselbe Kommando wird man den Geschwindigkeitszuwachs deutlich spüren.

Zur Anzeige der Hashtabelle ist »hash« ohne Angabe von Argumenten aufzurufen:

hash

hits    command
   2    /bin/ls
   1    /bin/mv
   6    /bin/sh
   8    /usr/bin/vi
   1    /bin/chmod
   3    /bin/date
   1    /usr/bin/id
   1    /usr/bin/man

Ein Problem besteht nun mit gleichnamigen Kommandos. Liegt eines in der Hashtabelle vor, so kann auf das andere nur über die vollständige Pfadangabe zugegriffen werden.

Soll dieses "benachteiligte" Kommando nun vermehrt eingesetzt werden, ist ein Löschen der Hashtabelle mittels »-r« sinnvoll:

hash -r

hash
hash: hash table empty

Indem »hash« mit einem oder mehreren Kommandonamen aufgerufen wird, werden diese Kommandos gesucht und in die Hashtabelle aufgenommen; sie werden jedoch nicht gestartet (sinnvoll ist dies eventuell in Startskripten).

Des Weiteren kann mit »-p <Pfad_zum_Kommando>« ein solches mit vorgegebenem Pfad der Tabelle hinzufügen.

8.27 help

Schreibt einen kurzen Hilfetext zu einem eingebauten Kommando aus.

8.28 history

Das Kommando dient zur Anzeige oder Manipulation des Kommandozeilenspeichers.

Ohne Optionen gerufen, werden alle Einträge der Liste inklusive einer Zeilennummerierung aufgeführt; mit einer vorangestellten Zahl kann die Darstellung auf die letzten Einträge eingeschränkt werden:

history 5

  555 parseline -aF -f
  556 parseline -f bla -l huch
  557 mv parseline Scripts\&Programs/
  558 ll Linuxfibel/bash.htm
  559 history 5

Anhand der Nummerierung kann nun gezielt ein Eintrag entfernt werden (»-d <Nummer>«). »-c« löscht den gesamten Inhalt.

Der Kommandozeilenspeicher wird bei Beendigung der Shell in einer Datei gesichert, um diese explizit zu aktualisieren, kann »-a« bzw. »-w« genutzt werden, womit die neuen Einträge angehangen werden bzw. der alte Inhalt ersetzt wird.

8.29 jobs

Das Kommando zeigt die in der Jobtabelle erfassten Jobs (Hintergrundprozesse) an.

Das Format der Ausgabe kann über Optionen gesteuert werden; im Zusammenhang mit Prozessen gehen wir weiter unten auf dieses Thema ein.

8.30 kill

Das Kommando dient der Steuerung bereits laufender Prozesse, indem es an diese Signale versendet.

Während einige Signale vordefinierte Bedeutungen besitzen, können andere von den Prozessen nach eigenen Vorstellungen behandelt werden.

Das Versenden von Signalen soll auch im Zusammenhang mit Prozessen diskutiert werden.

8.31 let

Für jedes Argument wird eine arithmetische Substitution versucht.

Expandiert das letzte Argument zu 0, ist der Rückgabewert 1; sonst immer 0. »let« bietet somit eine Möglichkeit der Überprüfung, ob eine Variable eine Zahl >0 enthält:

failure=1234x

let $failure 2>/dev/null || echo "keine Zahl"
keine Zahl
let answer=6*7
42

8.32 local

Das Kommando kann nur innerhalb von Funktionen verwendet werden und dient der Definition lokaler Variablen. Somit ist sichergestellt, dass existierende Variablen gleichen Namens nichtversehentlich überschrieben werden:

var=foo

func() { var=bla; echo $var; }
func
bla
echo $var
bla
var=foo
func() { local var=bla; echo $var; }
func
bla
echo $var
foo

8.33 logout

Beendet eine Login-Bash und meldet den Benutzer ab.

In einer Nicht-Login-Bash hagelt es eine Fehlermeldung.

8.34 popd

Entfernt den obersten Eintrag vom Verzeichnisstack und wechselt zum neuen obersten Verzeichniseintrag. Dieser Wechsel kann mit der Option »-n« unterdrückt werden.

Um einen anderen Eintrag als den obersten zu entfernen, kann dieser mit »+Anzahl« angegeben werden. Der oberste Verzeichniseintrag selbst ist »+0«, der zweite »+1« usw.

Mit »-Anzahl« beginnt die Zählung am unteren Ende des Stacks. Ein Beispiel zur Anwendung folgt beim korrespondierenden »pushd«.

8.35 printf "FORMAT" Argument[e]

Das Kommando verhilft zu einer formatierten Ausgabe analog zum printf der Programmiersprache C.

Die FORMAT-Zeichenkette enthält hierfür Platzhalter mit optionalen Ausrichtungsparametern; die nachfolgenden Argumente müssen vom Typ her genau dem Typ des Platzhalters entsprechen.

Innerhalb der FORMAT-Zeichenkette können Escape-Sequenzen verwendet werden. Die wichtigsten sind »\n« (Zeilenumbruch), »\t« (Tabulator) und »\a« (akustisches Zeichen).

Ein Platzhalter besitzt die Form »%[Ausrichtung]Symbol«, wobei die Ausrichtung eine Zahl ist, die die Anzahl darzustellender Zeichen des Arguments betrifft.

Ist das Argument länger, werden die überschüssigen Zeichen abgeschnitten, ist es kürzer, werden Leerzeichen aufgefüllt. Mit einem optionalen Minus »-« vor der Zahl wird das Argument linksbündig angeordnet.

Wichtige Symbole sind:

d Eine ganze Zahl
s Eine Zeichenkette
f Rationale Zahl; hierbei kann die Anzahl darzustellender Vor- und Nachkommastellen angegeben werden: "%8.3f"
E Darstellung rationaler Zahlen in Exponentenform.

Beispiele

printf "Zeichenkette: %8s Zahl %d\n" test 42

Zeichenkette:     test Zahl 42
printf "Zeichenkette:\t%8s\tZahl %d\n" test 42
Zeichenkette:       test        Zahl 42
printf "Zeichenkette:\t%-8s\tZahl %3.1E\n" test 42
Zeichenkette:   test            Zahl 4,2E+01

8.36 pushd

Mit dem Kommando kann in ein angegebenes Verzeichnis gewechselt werden, wobei das Verzeichnis auf einem Stack abgelegt wird.

Mit »-n« wird der Verzeichniswechsel verhindert, der Name des Verzeichnisses aber dennoch gespeichert. Dieser Stack kann rotiert werden, mit »-Anzahl« wird vom Ende des Stacks aus rotiert; mit »+Anzahl« vom Anfang.

Das nachfolgende Beispiel zählt alle Unterverzeichnisse ausgehend vom Startverzeichnis, wobei popd und pushd Verwendung finden:

cat countdir

#!/bin/sh

while :; do
  for i in $(ls); do
    test -d $i || continue
    pushd -n $(pwd)/$i &>/dev/null
    number=$(($number+1))
  done
  popd &>/dev/null && continue
  break
done
echo Anzahl: $number

8.37 pwd

Gibt das aktuelle Arbeitsverzeichnis aus. Mit der der Option »-P« wird der Pfad ohne enthaltene symbolische Links angegeben; mit »-L« werden Links berücksichtigt.

Beide Optionen sind sinnvoll, um die Einstellung der Variablen »physical« zu überschreiben:

pwd

/usr/X11
pwd -P
/usr/X11R6
set -o physical
pwd
/usr/X11R6

8.38 read Variable [Variable]

Mit »read« wird eine Eingabezeile eingelesen und deren Inhalt Variablen zugewiesen.

Die Eingabe wird anhand der in IFS vorgegebenen Trennzeichen in einzelne Token zerlegt und der Reihe nach den Variablen zugewiesen.

Stehen mehr Token zur Verfügung als Variablen, so wird die letzte Variable mit allen noch nicht zugewiesenen Token belegt; stehen weniger Token bereit, bleibt der Inhalt der überschüssigen Variablen leer:

read a b c

1 2 3 4 5
echo "a=$a"; echo "b=$b"; echo "c=$c"
a=1
b=2
c=3 4 5

Ist die Anzahl erwarteter Token nicht vorhersehbar, bietet sich die Verwendung einer Feldvariablen an. Hierzu ist dem Variablennamen einzig die Option »-a« voranzustellen:

read -a feld

Jedes Wort gelangt in ein eigenes Feld.
echo "3.Element: ${feld[2]}"
3.Element: gelangt

Für die Shellprogrammierung sind zwei weitere Optionen nützlich. Zum einen »-p Prompt«, womit die erwartete Eingabe durch ein Prompt signalisiert wird und »-t Timeout«, wodurch das Kommando nach Ablauf der angegebenen Zeitspanne (Sekunden) mit einem Fehlerwert zurück kehrt:

read -p "Eingabe: " -t 5 || echo "nichts gedrückt"

# Finger weg von der Tastatur!
Eingabe: nichts gedrückt

8.39 readonly

Variablen und Funktionen lassen sich nachträglich als »nicht änderbar« deklarieren.

Um eine einfache Variable nur-lesend zu vereinbaren, genügt die Angabe ihres Namens; bezieht man sich auf eine Funktion, so ist »-f Funktionsname« anzugeben.

Eine Feldvariable bewahrt die Option »-a Feldvariable« vor versehentlichem Überschreiben. Wird »readonly« ohne Angabe von Optionen oder Variablennamen aufgerufen, erhält man eine Auflistung aller »read-only«-Variablen und -Funktionen.

8.40 return [n]

Dient zum Rücksprung aus einer Funktion. Mit [n] kann ein Rückgabewert vereinbart werden, ansonsten wird der Status des zuletzt innerhalb der Funktion ausgeführten Kommandos geliefert.

8.41 set

set [Optionen] [Parameterliste]

Setzen von Shell-Optionen und Positionsparametern ($1 ... $n). Einige Optionen: * v Gibt die eingelesenen Shell-Eingaben auf dem Bildschirm aus.

  • x Gibt alle Kommandos vor der Ausführungen aus (--> zeigt Ersetzungen).
  • n Liest die Kommandos von Shell-Skripten, führt sie jedoch nicht aus.

Der Aufruf von set ohne Parameter liefert die aktuelle Belegung der Shell-Variablen. Außerdem kann set verwendet werden, um die Positionsparameter zu besetzen.

set eins zwei drei vier besetzt die Parameter mit $1=eins, $2=zwei, $3=drei und $4=vier. Da dabei auch Leerzeichen, Tabs, Zeilenwechsel und anderes "ausgefiltert" wird (genauer alles, was in der Variablen IFS steht), ist set manchmal einfacher zu verwenden, als die Zerlegung einer Zeile mit cut.

Die Belegung der Parameter kann auch aus einer Variablen (z. B. set $VAR) oder aus dem Ergebnis eines Kommandoaufrufs erfolgen. Beispiel:

set `date` # $1=Fri $2=Apr $3=28 $4=10:44:16 $5=MEZ $6=1999

echo "Es ist $4 Uhr"
Es ist 10:44:16 Uhr

Aber es gibt Fallstricke. Wenn man beispielsweise den Output von "ls" bearbeiten möchte, gibt es zunächst unerklärliche Fehlermeldungen (set: unknown option):

ls -l > foo

echo "Dateiname Laenge"
while read LINE
 do
 set $LINE
 echo $9 $5
done < foo
rm foo

Da die Zeile mit dem Dateityp und den Zugriffsrechten beginnt, und für normale Dateien ein "-" am Zeilenbeginn steht, erkennt set eine falsche Option (z. B. "-rwxr-xr-x"). Abhilfe schafft das Voranstelle eines Buchstabens:

ls -l > foo

echo "Dateiname Laenge"
while read LINE
 do
 set Z$LINE
 echo $9 $5
done < foo
rm foo

Weitere Beispiele: Wenn ein Benutzer eingeloggt ist, wird ausgegeben seit wann. Sonst erfolgt eine Fehlermeldung.

if HELP=`who | grep $1`

then
 echo -n "$1 ist seit "
 set $HELP
 echo "$5 Uhr eingeloggt."
else
 echo "$1 ist nicht auffindbar"
fi

Ersetzen der englischen Tagesbezeichung durch die deutsche:

set `date`

case $1 in
 Tue) tag=Die;;
 Wed) tag=Mit;;
 Thu) tag=Don;;
 Sat) tag=Sam;;
 Sun) tag=Son;;
 *) tag=$1;;
esac
echo $tag $3.$2 $4 $6 $5

set

Dient zum Setzen bash-interner Variablen, die das Verhalten der Shell maßgeblich beeinflussen. Nähere Informationen wurden bereits weiter oben gegeben.

shift [n]

Dient zum Verschieben der Positionsparameter ; ohne weitere Angaben wird die Liste der Positionsparameter um eine Stelle nach links verschoben; mit Angabe eine Ziffer wird um die spezifizierte Anzahl Stellen rotiert.

8.42 shopt

Dient zum Setzen bash-interner Variablen, die das Verhalten der Shell maßgeblich beeinflussen. Nähere Informationen wurden bereits weiter oben gegeben.

8.43 suspend

Suspendiert die Shell. Sie kann nur durch ein Signal SIGCONT reaktiviert werden.

8.44 test Ausdruck

test liefert in Abhängigkeit vom Wahrheitswert des Ausdrucks 0 (wahr) oder 1 (falsch) zurück und ist damit ein wichtiger Bestandteil vieler Shellskripten.

Es existieren mannigfaltige Klassen von Tests. Doch bevor wir Ihnen eine Auswahl verfügbarer Tests vorstellen, möchten wir auf eine alternative Schreibweise hinweisen, die gern bei bedingter Ausführung eingesetzt wird.

Im nachfolgenden Beispiel sind die beiden Zeilen semantisch äquivalent:

test -z $DISPLAY

[ -z $DISPLAY ]

Beachten Sie, dass nach der öffnenden eckigen Klammer und vor der schließenden zwingend ein Whitespace stehen muss!

Die wohl wichtigsten Tests befassen sich mit Dateien(alle Tests schließen einen Existenztest der Datei mit ein):

-b/-c Test auf Gerätedatei (Block/Character): test -b - /dev/hda; echo $?
0
test -b /dev/console; echo $?
1
-d Test auf Verzeichnis: for i in $(ls|head -5); do
>(test -d $i && echo "$i ist ein Verzeichnis")
>|| echo "$i ist kein Verzeichnis"
> done
Desktop ist ein Verzeichnis
Linuxfibel ist ein Verzeichnis
Systemprogrammierung ist ein Verzeichnis
allekapitel.htm ist kein Verzeichnis
amanda.rpm ist kein Verzeichnis
-e Existenz der Datei
-f Test auf normale Datei: test -f /dev/hda; echo $?
1
-k Test, ob das »sticky«-Flag auf ein Verzeichnis gesetzt ist: test -k /tmp; echo $?
0
-p Test auf Pipe: test -p /dev/xconsole; echo $?
0
-r/-w/-x Test auf Lese-/Schreib-/Ausführungsrecht
-s Test, ob eine Datei nicht leer ist
-u Test, ob das »suid«-Flag auf einer Datei gesetzt ist: test -u /usr/bin/passwd; echo $?
0

Datei_1 -nt Datei_2 bzw. Datei_1 -ot Datei_2

Test, ob Datei_1 »neuer« bzw. »älter« ist als die Datei_2

Datei_1 -ef Datei_2

Test, ob Datei_1 und Datei_2 den selben Inode auf demselben Device besitzen (die eine Datei ist ein harter Link auf die andere):

ln bla foo

test bla -ef foo; echo $?
0

Des Weiteren existieren eine Reihe von Operatoren zum Vergleich von Zeichenketten:

-z Zeichenkette

Der Test ist wahr, wenn die Zeichenkette die Länge 0 hat

-n string

Der Test ist wahr, wenn die Länge der Zeichenkette >0 ist

Zeichenkette_1 == Zeichenkette_2

Wahr, wenn die Zeichenketten gleich sind

Zeichenkette_1 != Zeichenkette_2

Wahr, wenn die Zeichenketten ungleich sind

Zeichenkette_1 < Zeichenkette_2

Wahr, wenn die Zeichenkette_1 lexikalisch kleiner ist als Zeichenkette_2

Zeichenkette_1 > Zeichenkette_2

Wahr, wenn die Zeichenkette_1 lexikalisch größer ist als Zeichenkette_2

Eine vielseitige Anwendung ist der Vergleich von Argumenten mit den Operatoren -eq (gleich), -ne (ungleich), -gt (größer als), -lt (kleiner als), -ge (größer als oder gleich) und -le (kleiner als oder gleich):

  1. Überprüfung der Anzahl Parameter in einem Shellskript...
if [ "$#" -lt "3" ]; then echo "Zu wenige Parameter"; exit 1; fi

Anmerkung: Die Prüfung und Beendigung eines Skripts im Fehlerfall lässt sich eleganter über die Parametersubstitution realisieren:

  1. Elegante Überprüfung der Anzahl Parameter in einem Shellskript...
var_3 = ${3:?Zu wenige Parameter}

Mehrere Tests können kombiniert werden:

! Negation

-a Logisches UND zweier Tests

-o Logisches ODER zweier Tests

test -b /dev/null -o -c /dev/null

test $? -eq 0 && echo "Gerätedatei"
Gerätedatei

8.45 times

Misst die Zeit für die Shell und der von dieser gestarteten Kommandos.

Ausgegeben wird die User- und die Systemzeit (nicht jedoch die Realzeit, dies vermag das Kommando time):

times ls 0m0.090s 0m0.030s 0m0.160s 0m0.160s

8.46 trap

trap 'Kommandoliste' Signale

Ausführen der Kommandoliste, wenn eins der angegebenen Signale an den Prozeß (= Shell) gesendet wird.

Die Signale werden in Form der Signalnummern oder über ihre Namen (SIGKILL, SIGHUP, ...), getrennt durch Leerzeichen aufgeführt.

Ist die Kommandoliste leer, werden die entsprechenden Signale abgeschaltet. Bei einfachen Kommandos reichen oft auch die Anführungszeichen, um die Shell-Ersetzung zu verhindern.

Signale sind eine Möglichkeit, wie verschiedenen Prozesse, also gerade laufende Programme, miteinander kommunizieren können.

Ein Prozeß kann einem anderen Prozeß ein Signal senden (der Betriebssystemkern spielt dabei den Postboten). Der Empfängerprozeß reagiert auf das Signal, z. B. dadurch, dass er sich beendet. Der Prozeß kann das Signal auch ignorieren.

Das ist beispielsweise nützlich, wenn ein Shellskript nicht durch den Benutzer von der Tastatur aus abgebrochen werden soll.

Mit dem trap-Kommando kann man festlegen, mit welchen Kommandos auf ein Signal reagiert werden soll bzw. ob überhaupt reagiert werden soll.

Neben anderen können folgende Signalnummern verwendet werden:

0 SIGKILL Terminate (beim Beenden der shell)
1 SIGHUP Hangup (beim Beenden der Verbindung zum Terminal oder Modem)
2 SIGINT Interrupt (wie Strg-C-Taste am Terminal)
3 SIGQUIT Abbrechen (Beenden von der Tastatur aus)
9 SIGKILL Kann nicht abgefangen werden - Beendet immer den empfangenden Prozeß
15 SIGTERM Terminate (Software-Terminate, Voreinstellung)

Eine Liste aller Signale gibt folgendes Kommando aus:

kill -l

Beispiele

  1. Skript sperren gegen Benutzerunterbrechung:
trap "" 2 3

oder auch

  1. Skript sauber beenden
trap 'rm tmpfile; cp foo fileb; exit' 0 2 3 15

Bitte nicht das exit-Kommando am Schluss vergessen, sonst wird das Skript nicht beendet. Wiedereinschalten der Signale erfolgt durch trap [Signale]. Ein letztes Beispiel zu trap:

  1. Automatisches Ausführen des Shellskripts .logoff beim
# Ausloggen durch den folgenden Eintrag in .profile:
trap .logoff 0

trap

Viele Programme lassen sich während der Laufzeit über bestimmte Signale beenden oder unterbrechen (bspw. schießt [Strg]+[C] fast jedes Programm ab).

»trap« kann nun auf zwei Arten verwendet werden.

Zum einen kann ein Signal (außer SIGKILL, Nr. 9) von der Behandlung durch die Bash ausgeschlossen werden. Dazu lässt man trap ein paar leerer Anführungsstriche und die Liste der zu ignorierenden Signalnummern (die Nummern erhält man mit "kill -l") folgen:

sleep 100

[Strg]+[C]   # Sofortiger Abbruch durch Signal SIGINT (Nr.2)

trap "" 2
'# Die Bash interessiert sich nun nicht mehr für SIGINT: sleep 100
[Strg]+[C]   # Kein Abbruch

Die ursprüngliche Bedeutung eines Signals kann durch die Angabe von »trap Signalnummer« wieder hergestellt werden.

Die zweite Verwendung von »trap« erlaubt die Verbindung von Kommandos mit bestimmten Signalen. Trifft ein solches ein, wird das vereinbarte Kommando aufgerufen:

trap 'echo "SIGINT empfangen"' 2

[Strg]+[C]SIGINT empfangen

dirkw@lincln01>

trap ist somit für Shellskripte interessant, die mit temporären Dateien arbeiten, um diese bei Abbruch durch ein Signal ordnungsgemäß aufräumen zu können.

Die Option -p bringt alle von trap veränderten Signale mit ihrer Belegung zum Vorschein.

8.47 type

Das Kommando verrät »etwas mehr« über ein als Argument angegebenes Kommando. Ohne Option verrät es, ob das Argument ein builtin-Kommando, eine Funktion,... oder ein auf der Platte liegendes Programm ist:

type test

test is a shell builtin

type passwd
passwd is /usr/bin/passwd

Etwas bedeckt hält sich type -t, was die Art des Arguments mit einem knappen Wort erklärt:

type -t test

builtin

type -t passwd
file

-a lässt das Kommando alle Interpretationen ausgeben und ist bei der Suche nach einem Kommando sicher eine nette Hilfe:

type -a test

test is a shell builtin
test is /usr/bin/test

Schließlich beschränkt die Option -p die Suche auf die auf der Platte gespeicherten Programme, in vielen Distributionen hat sich dafür which eingebürgert:

type which

which is aliased to `type -p'

8.48 typeset

Siehe declare

8.49 ulimit

Mit diesem Befehl können die von der Shell und aus dieser gestarteten Prozesse verfügbaren Ressourcen beschränkt werden.

Der Administrator kann durch Verwendung des Kommandos in der /etc/profile einem jeden Benutzer, dessen Login-Shell die Bash ist, unwiderrufliche Schranken auferlegen.

Der Benutzer kann diese Werte dann nur verringern; aber niemals erhöhen (dies darf einzig Root).

Jede Ressource kann durch zwei Werte beschränkt werden. Das Softlimit kann überschritten werden, während die Anforderung von Ressourcen, die das Hardlimit überschreiten mit einer Fehlermeldung abgewiesen werden. Softlimit setzt man mit der Option -L, das Hardlimit mit -H; ohne Option werden beide Limits mit ein und denselben Wert belegt.

Die Option -a bringt alle Ressourcen und die Softlimits zum Vorschein:

ulimit -a

core file size (blocks)     0           # -c
data seg size (kbytes)      unlimited   # -d
file size (blocks)          unlimited   # -f
max locked memory (kbytes)  unlimited   # -l
max memory size (kbytes)    unlimited   # -m
open files                  1024        # -n
pipe size (512 bytes)       8           # -p
stack size (kbytes)         unlimited   # -s
cpu time (seconds)          unlimited   # -t
max user processes          1024        # -u
virtual memory (kbytes)     unlimited   # -v

Hinter den einzelnen Ausgabezeilen haben wir die Optionen eingefügt, die Sie zur Manipulation eines konkreten Limits angeben müssen.

Vielleicht ist zu dem einen oder anderen Limit noch eine Anmerkung notwendig, da vermutlich nicht jeder Leser ein Programmierer ist.

Eine core-Datei ist ein Speicherauszug, also ein Abbild des RAM. Linux schreibt den Speicherbereich eines Prozesses, falls jener mit einem schwerwiegenden Fehler abbricht (z.B. Speicherschutzverletzung) in eine Datei, die anschließend mittels eines Debuggers nach der Fehlerursache hin untersucht werden kann.

Dazu bedarf es allerdings weit reichender Programmiererfahrungen. Da diese Cores durchaus mehrere MByte groß werden können, empfiehlt sich deren Anlegen zu unterbinden (Wert 0).

Zu jedem Prozess gehören neben dem im RAM liegen0,10cmden Programm noch ein Datensegment für (der Name sagt's schon) die Daten und ein Stack, der u.a zur Wertübergabe zwischen Funktionen verwendet wird. Wer nicht genau weiß, was er tut, tut gut daran, mit diesen Limits nichts zu tun.

Die weiteren Werte sollten selbsterklärend sein. Der Systemadministrator ist berechtigt, ein Limit aufzuheben, indem er an Stelle des Wertes das Schlüsselwort unlimited angibt.

Abschließend sei noch erwähnt, dass die vom System vorgegebenen Grenzen mit den hiesigen Werten nicht manipuliert werden können; dies geht nur durch Erzeugung eines neuen Kernels oder durch Änderungen einiger Parameter zur Laufzeit (siehe Prozessdateisystem).

8.50 umask

Setzt die Rechtemaske für neu erzeugte Dateien oder Verzeichnisse bzw. zeigt die Rechte an. Die Option -S veranlasst die Verwendung einer symbolischen anstatt der voreingestellten nummerischen Angabe:

umask -S

u=rwx,g=rx,o=rx

Weitere Beispiele finden Sie im Abschnitt Zugriffsrechte.

8.51 unalias

Löscht den angegebenen Alias bzw. mit der Option -a alle Aliasse.

8.52 unset

Dient zum Löschen einer Variable oder Funktion.

8.53 wait

Dient zum Warten auf einen oder alle Hintergrundprozesse. Wird keine Prozess- oder Jobnummer angegeben, so wird auf die Terminierung aller Hintergrundprozesse gewartet und der Rückgabewert ist 0. Mit Spezifizierung einer Nummer wird genau auf den betreffenden Prozess gewartet und dessen Rückgabestatus ist der Rückgabewert von »wait«.

9 Quellen / weitere Informationen

  • bash-Dokumentation (www.gnu.org/software/bash/)
  • man: bash
  • Linuxfibel (de.linwiki.org/wiki/Linuxfibel)
  • Oreilly: Einführung in die bash-Shell
  • Bash Guide for Beginners (tldp.org/LDP/Bash-Beginners-Guide)