Druckversion | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Was ist die Bourne Again Shell?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 nach zogen. Entscheidend scheint doch eher 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. Die Bash wird von der Free Software Foundation entwickelt und ist die Standardshell des Gnu-Betriebssystems HURD. Mit der Ende 1996 erschienenen Version 2.0 haben sich grundlegende Dinge gegenüber dem Vorgänger geändert. Wir werden uns ausschließlich auf die Bash >2.0 beziehen; einige Gaben sind gar dem neuesten Vertreter entliehen. Aktuelle Version ist 2.0.4. Gliederung dieses AbschnittsVorab ein paar Worte zu den Überschriften und was Sie unter den einzelnen Punkten finden. Die Fähigkeiten der Bash und Definitionen stellen einen Versuch der Erklärung dar, warum so zahlreiche Anwender die Bash als "die tolle Shell" an sich ansehen. Enthalten sind auch einige Begriffe, die wir nachfolgend verwenden. Der Erklärungsbedarf besteht, weil es uns nicht gelang, für manche Bezeichnung einen adäquaten deutschen Ausdruck zu finden. Jedes nützliche Programm wird einmal gestartet werden. So beginnt die Exkursion mit dem Start der Bash. Hierbei geht es uns um die Optionen, über die die Bash beim Aufruf gesteuert werden kann und um interne Abläufe, die zumeist unbemerkt im Hintergrund geschehen. Mit dem Erscheinen der ersten Eingabeaufforderung hat die Startmaschinerie ihre Aufgabe erfüllt und hier endet auch der Einblick dieses Abschnitts. Und beim Beenden der Bash geschehen auch noch Dinge, von denen der Anwender selten etwas bemerkt... Syntax klingt stark nach trockener Theorie. Und mit jener beschäftigt sich auch dieser Abschnitt. Der Schwerpunkt liegt auf »korrekten Eingaben«. So wird bspw. das Erzeugen, Manipulieren und Löschen von Variablen behandelt; mit keinem Wort wird allerdings der Wirkungsbereich von Variablen erwähnt; da dies kein syntaktisches Problem darstellt. Wesentlich ist das Verstehen der Betrachtung der Kommandozeile durch die Bash. Syntax zeigt auf, nach welchen Regeln die Shell die Eingabe bearbeitet, beschränkt sich jedoch auf die Benennung der Mechanismen. Expansionen schließlich durchleuchtet Regel für Regel und erschließt dem ambitionierten Shellprogrammierer sein künftiges Betätigungsfeld. Initialisierungen behandelt alle Methoden, die zur Konfiguration der Laufzeitumgebung der Bash beitragen. Hierunter fallen die Konfigurationsdateien, aber auch die Variablen - diesmal im Kontext ihrer Wirkungsweise. Bash-interne Variablen aktivieren bestimmte Verhaltensweisen oder schalten sie ab (sollte Ihre Bash abweichende Reaktionen zeigen, könnte eine Shelloption dafür verantwortlich zeichnen). Des Weiteren gliedern sich Aliasse, Funktionen und eingebaute Kommandos in diesen Abschnitt ein. Mit der Interaktiven Bash bewerten wir Methoden zur Navigation und Manipulation der Kommandozeile. Die Eingabehilfen passen ebenso in diese Thematik wie die Handhabung von Prozessen. History wäre auch bei der Interaktiven Bash bestens aufgehoben. Jedoch sind die Möglichkeiten so weit reichend, dass wir dem Kommandozeilenspeicher einen eigenen Abschnitt widmen.
Wichtige FähigkeitenDer Funktionsumfang der Bash ermöglicht sowohl ein komfortables Arbeiten als auch die Verrichtung komplexer Aufgaben. Hierzu zählen:
Einige DefinitionenWas es mit den knappen Aussagen konkret auf sich hat, soll Gegenstand der folgenden Abschnitte sein. Zuvor sollen benötigte Begriffe kurz erläutert werden. 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
Eine Zeichenfolge, die von der Shell als eine Einheit betrachtet wird.
Whitespace Steht für Leerzeichen und Tabulatoren und ggf. dem Zeilenumbruch.
Abbildung 1: Unterteilung der Bash Interaktive und Nicht-Interaktive BashIst eine Shell mit der Standardein- und Standardausgabe (sprich »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; letztere Bash schränkt die Befugnisse des Anwenders 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. Optionen beim StartDie 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: -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:
(Wird der Bash ein Argument übergeben, das keine Option ist, so interpretiert sie das Argument als Datei, die die Kommandos enthält. »date« ist natürlich keine Datei, die Kommandos beinhaltet, also beschwert sich die Bash...). Die Bash in Verbindung mit »-c« arbeitet als nicht-interaktive Shell. -r bzw. --restricted Die Bash arbeitet als »Restricted« Shell (siehe weiter unten).
-i Die Bash arbeitet als interaktive Shell, d.h. die Standardein- und -ausgabe sind mit einem Terminal verbunden.
--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.).
--noprofile Unterdrückt das Einlesen der profile-Dateien.
--norc Unterdrückt das Einlesen der »~/.bashrc«.
--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.
--rcfile Datei Eine nicht-interaktive Bash liest die angegebene Datei ein (anstatt ~/.bashrc)
-v bzw. --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 - behandeln wir die Flags erst im Zusammenhang mit dem builtin-Kommando set. Bash als Login-ShellIn 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 beschriebenem 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:
Restricted BashMit dem Argument -r gestartet, arbeitet die Bash als restricted Shell. Dasselbe Verhalten kann erreicht werden, wenn ein Link rsh bzw. rbash auf die Bash verweist und diese über den Link gerufen wird. Das Anliegen einer »eingeschränkten Shell« ist, einen Anwender in seinen Befugnissen weiter einzuschränken. Unter einer restricted Bash ist es nicht möglich:
Bei den Einschränkungen scheint eine Erlaubnis zum Zugang einer »nicht-vertrauenswürdigen« Person unkritisch zu sein. Doch Vorsicht! Stellen Sie sicher, dass die exportierte PATH-Variable keine Verzeichnisse enthält, die eine Shell enthalten. Es ist durchaus sinnvoll, zu diesem Zweck ein eigenes Binaryverzeichnis mit »unkritischen« Kommandos (analog zu FTP) anzulegen und einzig dieses in PATH aufzunehmen. Allerdings gibt es eine Reihe Kommandos (bspw. Editoren), die intern das Eröffnen einer Shell unterstützen und somit das Umgehen der Restriktionen ermöglichen. So ist es erlaubt, im vi eine »normale« Bash zu starten, selbst wenn der Editor innerhalb einer restricted Shell gestartet wurde (neuere Versionen starten wiederum nur eine restricted Bash)!
Handelt es sich um eine Login-Shell, so lässt sich die Bash sogar vor ihrem Ende zu einem letzten Groß-Reine-Machen motivieren. Alle Aktionen, die die Shell vor ihrem Ableben noch erledigen soll, sind hierzu in eine Datei .bash_logout zu schreiben. 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. Die Bash muss nicht unbedingt verlassen werden. Ein Ersetzen des Programms durch ein anderes mittels exec kommt einer Beendigung gleich. exec wird im Zusammenhang mit eingebauten Kommandos behandelt.
Es ist schier unmöglich, ein komplexes Thema wie die Bash »in einem Fluss« darzulegen. Nahezu jedes Konzept steht in Wechselwirkung mit einem anderen, nicht unwesentlicheren Aspekt. Mit welchem sollte man beginnen? Bevor uns die praktischen Dinge der Bash den Kopf zerbrechen, steht deshalb die (trockene) Theorie der allgemeinen Syntax voran, natürlich garniert mit (hoffentlich) sinnvollen Beispielen. Und hier erwächst ein weiteres Problem. Wie soll ein Beispiel praktisch sein, wenn wir uns auf das bisherige Wissen beschränken? Indem wir die Schranken missachten und den Leser auf den weiteren Text verweisen, der die fehlenden Wissenslücken schließen wird. Der weitere Text erklärt:
KommentareNormalerweise 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 Quoten (siehe später) oder durch die Shelloption "interactive_comments" aufgehoben werden.
VariablenDer Bezeichner einer Variable ist ein Name (vergleiche Definitionen). Die Anzahl Zeichen, die für einen Bezeichner signifikant sind, ist von der konkreten Bash-Version abhängig. Sie können aber davon ausgehen, dass Sie beim Eingeben eines langen Bezeichners eher ermüden, als dass die Bash kapituliert. Im Sinne der Syntax werden skalare und Feldvariablen unterschieden. Zum Erzeugen von Variablen existieren mehrere Möglichkeiten. Die einfachste zum Anlegen einer skalaren Variable ist:
Eine Feldvariable (Array) erzeugen Sie mittels
Den Inhalt einer Variable betrachten Sie mit Hilfe des Kommandos echo:
Bemerkung: 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]«. Eine Variable können Sie mit einem Attribut versehen. Hierzu verwenden Sie declare oder typeset. Beide Kommandos besitzen dieselben Optionen und Wirkungen - wir beschränken uns daher auf ersteres. 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:
[-/+] i Setzt/Löscht das Integer-Attribut. Für so deklarierte Variablen ist eine einfachere Syntax für Arithmetik erlaubt.
-p Zeigt die Attribute und den Inhalt einer Variablen an.
[-/+] 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:
[-/+] 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).
Eine Variable kann auch gelöscht werden. Hierzu dient das builtin-Kommando unset:
Expansion vor der Wertzuweisung an eine VariableBevor 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:
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. Einfache KommandosDer Begriff des »einfachen Kommandos« definiert einen wichtigen Aspekt der Semantik der Bash. Ein »einfaches Kommando« ist ein Ausschnitt der Kommandozeile, den die Bash in einem Zusammenhang betrachtet. Dazu parst die Shell zunächst 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 (Definitionen). 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. Anmerkung: 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:
Expansion von WortenJede durch Metazeichen getrennte Zeichenkette bildet ein einzelnes Wort in der Eingabe und wird von der Bash nach bestimmten Regeln expandiert. Die Reihenfolge der Auswertung ist:
Was sich hinter den einzelnen Expansionen verbirgt, soll im Anschluss an diesen Abschnitt betrachtet werden. Rückgabestatus eines KommandosMit 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 $?.
PipeliningIn einer Pipeline werden mehrere Kommandos mittels des Pipesymbols | miteinander verknüpft. Jedes Kommando wird hierbei in einer eigenen Prozessumgebung ausgeführt. Das Kommando links eines Pipesymbols schreibt seine Ergebnisse nachfolgend in die Pipe (»Röhre«) anstatt auf die Standardausgabe. 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. Die vermutlich verbreitetste Anwendung von Pipelines ist die Umleitung von Ausgaben eines Kommandos in einen Pager, um diese bequem seitenweise betrachten zu können:
Ohne den Pager hätte man in obigem Beispiel keine Möglichkeit, die ca. 1500 Ausgabezeilen von ls am Bildschirm zu verfolgen. Eine komplexere Anwendung ist die nächste Verknüpfung zweier tar-Befehle, womit ein ganzes Verzeichnis bei Beibehaltung der Eigentümer kopiert wird:
ListenIn Erweiterung des Pipeline-Konzepts lassen sich mehrere Kommandos auf weitere Arten in einer Kommandozeileneingabe angeben. Im Sprachgebrauch der Bash werden diese Varianten als Listen zusammengefasst: 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.
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:
Vielleicht sollte die Datei »bla« zur Demonstration des Beispiels angelegt werden? Allerdings nur, wenn sie noch nicht existiert:
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.
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:
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):
{ Kommando;} Startet das Kommando innerhalb der aktuellen Shell. Sinn dieses Konstrukts ist die Gruppierung mehrerer Kommandos, sodass sie eine gemeinsame Ausgabe erzeugen:
Erläuterung: 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. (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:
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:
Ein/AusgabeumleitungMit dem Start der Bash öffnet diese die drei Standarddateien mit den Dateideskriptoren 0 (stdin), 1 (stdout) und 2 (stderr). Die Standardeingabe ist dabei zunächst mit der Tastatur verbunden, Standard- und Standardfehler-Ausgabe schreiben initial auf den Bildschirm. Mit den Mechanismen der E/A-Umleitung lassen sich alle drei Kanäle (genau genommen jeder beliebige Dateideskriptor) in Dateien umlenken, so dass ein Kommando seine Eingaben aus einer Datei bezieht oder die Ausgaben bzw. Fehler in eine solche gelenkt werden. Die Sonderzeichen zur E/A-Umleitung sind: [n]< Datei Umleitung der Standardeingabe. Rein interaktiv arbeitende Kommandos erwarten ihre Eingaben von der Standardeingabe. Bestes Beispiel ist das Kommando mail, das den Text einer Nachricht prinzipiell von der Standardeingabe liest. Mail möchte man allerdings häufig automatisch erzeugen und den Inhalt einer Datei versenden; hier kann die Eingabeumleitung die Lösung bedeuten:
Im Beispiel liest das Kommando mail tatsächlich von Dateideskriptor 0, wohinter sich normalerweise die Standardeingabe verbirgt. Möglich wird dies, indem die Bash vor der Kommandoausführung die Standardeingabe schließt, d.h. den Dateideskriptor 0 frei gibt, und der erste freie Deskriptor (also nun 0) beim anschließenden Öffnen der Datei "Message.txt" an diese vergeben wird. Links des Umleitungssymbols kann sogar das Ziel der Umleitung fehlen, dann steht der Inhalt der umgeleiteten Datei direkt in der Standardeingabe. Eine »sinnvolle« Anwendung ist mir bislang nicht in die Quere gekommen, deswegen soll die obskure Syntax eines Kopiervorgangs die Idee verdeutlichen:
[n]> Datei Umleitung der Ausgabe in eine Datei. Existiert die Zieldatei nicht, wird sie automatisch erzeugt. Ist sie bereits vorhanden, wird sie geöffnet und ihr alter Inhalt komplett verworfen (über Shelloptionen kann das Überschreiben verhindert werden; allerdings nicht, wenn anstatt »>« »>|« verwendet wird). Mit der optionalen Angabe von »n« kann der umzuleitende Deskriptor angegeben werden; ohne Angabe wird implizit »1« angenommen, was die Standardausgabe betrifft:
[n]>> Datei Umleitung der Standardausgabe, wobei die Ausgaben an die Zieldatei angehangen werden. Die Arbeitsweise ist analog zur »normalen« Ausgabeumleitung, außer dass im Falle einer existierenden Datei die neuen Daten an deren Ende angehangen werden. &> Datei bzw. >& Datei Umleitung der Standard- und Standardfehlerausgabe. Beide Varianten der Angabe sind semantisch äquivalent; das Manual zur Bash empfiehlt die erstere Form. Eine nützliche Anwendung dieser Umleitung findet man im Zusammenhang mit Shellskripten, wo man oftmals einzig am Rückgabewert eines Kommandos, nicht aber an dessen Ausgabe interessiert ist. Als Beispiel testet die folgende Kommandofolge, ob der Benutzer "tux" Mitglied der Gruppe "fibel" ist:
"Here-Document" Diese Form der Umleitung ermöglicht die Zusammenfassung mehrerer Zeilen als Eingabe für ein Kommando. Normalerweise ist mit Eingabe des Zeilenumbruchs die Eingabe für die Bash beendet, beim so genannten »Here-Document« wird eine Eingabe-Ende-Kennung vorab vereinbart und alle nachfolgenden Zeilen bis zum Erscheinen der Kennung auf einer neuen Zeile werden einem Kommando als Eingabe zugeführt. Die allgemeine Syntax lautet:
Als Endekennung können Sie jede Zeichenkette verwenden, die im folgenden Dokument nicht allein auf einer Zeile existiert. Die Endekennung wird dabei weder der Parameter-, der Kommando-, der Pfadnamen-, noch der arithmetischen Expansion unterzogen. Indem Sie die Kennung quoten, beugen Sie auch den anderen Expansionen vor. Wurde die Kennung nicht gequotet, werden die Zeilen des »Here-Documents« der Parameter-, der Kommando- und arithmetischen Expansion unterzogen, sonst wird keinerlei Expansion vorgenommen:
Sie werden sich fragen, wo man eine solche Anwendung sinnvoll anwenden könnte? Beispiele gibt es genügend, bspw. um aus einem Shellskript heraus eine Datei anzulegen. Vielleicht haben Sie bereits Software eigenständig kompiliert? Dann sind Ihnen sicher die »configure«-Aufrufe bekannt. Diese testen die Umgebung des Systems. U.a. stellen sie fest, welche Werkzeuge vorhanden sind und generieren daraus die späteren Makefiles. Das nachfolgende Skript testet, ob es sich bei dem installierten Kompiler »cc« um einen GNU-Compiler handelt. Es legt hierzu eine Datei mit einem »Define« an, das nur ein GNU-Compiler kennt. Anschließend wird der Präprozessor des Kompilers gestartet und in dessen Ausgabe gesucht, ob der durch das »Define« geschachtelte Ausdruck übernommen wurde:
Duplizieren von Dateideskriptoren Mit den bisherigen Mitteln war es nur möglich, zu einem Zeitpunkt aus einer Quelle in eine geöffnete Datei zu schreiben bzw. aus dieser zu lesen. D.h. ein Dateideskriptor war exklusiv nutzbar. Indem ein solcher Deskriptor verdoppelt wird, lässt sich diese Beschränkung aufheben. Die Eingabe lässt sich mit folgendem Konstrukt duplizieren:
Ohne Angabe von »n« ist die Standardeingabe gemeint, sonst kann »n« jeder zum Lesen geöffnete Deskriptor sein. Analog dazu verdoppelt folgende Angabe einen Ausgabedeskriptor:
Mit dem Mechanismus der Ausgabeverdopplung ließe sich die gleichzeitige Umleitung von Standardausgabe und -fehler auch wie folgt formulieren:
Erläuterung: Der Ausdruck »2>&1« kann wie folgt interpretiert werden: »Sende die Standardfehlerausgabe (2) dorthin, wohin die Standardausgabe (1) geht.«. Implizit wird eine Kopie des Ausgabedeskriptors erzeugt. Beachten Sie, dass die Bash die Umleitungen in der Kommandozeile von links nach rechts bewertet. Vertauschen Sie in obigem Beispiel die beiden Umleitungen:
so landen die Fehlermeldungen auf dem Bildschirm und nur die normalen Ausgaben in »outfile«. Zum Zeitpunkt der Umleitung der Fehler auf die Standardausgabe zeigt diese noch auf die »normale« Ausgabedatei (Terminal-Device). Die Fehler werden demzufolge auf eine Kopie des Deskriptors gelenkt, der das Terminal geöffnet hat. Erst nachfolgend wird der Originaldeskriptor auf die Datei »umgebogen«. Bleibt die Frage nach dem Nutzen derartiger Konstrukte... aber gerade das letzte Beispiel offenbart eine trickreiche und dennoch sinnvolle Anwendung. Die "normalen" Ausgaben laufen unter Unix i.d.R. gepuffert ab, einzig Fehler finden ohne Verzögerung ihren Weg auf die Konsole. Eine Variante, die Standardausgabe ungepuffert dem Bildschirm zukommen zu lassen, wäre, diese auf Standardfehler umzuleiten. Doch wäre es nun wünschenswert, die Fehler auf einen anderen Kanal zu dirigieren. Folgendes Beispiel realisiert die notwendigen Umleitungen:
Und wie leitet man die normalen und die Fehlerausgaben eines Kommandos in die Eingabe eines anderen? Mit folgender Notation:
Um einzig die Fehlerausgaben in eine Pipe zu speisen, hilft folgendes Vorgehen:
Öffnen von Deskriptoren zum Schreiben und Lesen
Eine Beispiel zur Anwendung wird im Zusammenhang mit dem eingebauten Kommando exec gegeben. Bedingte AusführungDie 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:
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).
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.
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, wir werden es später im Kontext der eingebauten Kommandos ausführlich kennen lernen. Details zu awk finden Sie im gleichnamigen Abschnitt. SchleifenDie 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.
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.:
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):
Mit Bash-Version 2.0.4 wurde die for--Schleife um eine an die Programmiersprache C angelehnte Syntaxvariante erweitert:
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.
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. 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:
Erläuterung: 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). Beispiel 2: Der Umsteiger von DOS 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:
Erläuterung: Die Generierung des Ziel-Dateinamens enthält eine Parametersubstitution. 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:
Beispiel 1: Die nachfolgende Schleife berechnet die Werte 2n für n=1..8:
Beispiel 2: Die folgende while-Schleife zählt, wie viele Benutzer die Gruppe "100" als default-Gruppe besitzen:
Erläuterung: 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 ([Ctrl][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" finden Sie 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. 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.
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:
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. 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:
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. Bestimmte Zeichenmuster werden hierbei durch andere substituiert - sie "expandieren". 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. An dieser Stelle soll nun die fehlende Beschreibung der einzelnen Expansionsmechanismen nachgeholt werden, wobei die Reihenfolge der Darstellung einzig der alphabetischen Anordnung entspricht!
ArithmetikDie Bash ist kein Taschenrechner. Dennoch besitzt sie ein erstaunliches Potenzial an eingebauten Rechenoperationen, die -- nach Prioritäten geordnet -- nachfolgende Tabelle zusammenfasst: + - 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
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:
Die so genannte arithmetische Substitution ist der gebräuchliche Weg, um Berechnungen durchzuführen:
Einige Beispiele sollen die Anwendung verdeutlichen:
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:
KlammernexpansionMit 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:
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:
KommandosubstitutionDie Kommandosubstitution erlaubt das Ersetzen ihres Aufrufes durch ihre Ausgabe. Es existieren zwei Syntaxvarianten des Aufrufs:
Die Bash führt das Kommando aus und ersetzt seinen Aufruf auf der Kommandozeile durch dessen Ausgabe, wobei abschließende Zeilenendezeichen entfernt wurden.
Eine solche Kommandosubstitution kann auch bei der Zuweisung an eine Variable angewendet werden:
Parameter- und VariablenexpansionFolgt 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:
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:
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.
PfadnamensexpansionBei dem als Pfadnamensubstitution bekannten Mechanismus durchsucht die Bash die Kommandozeile nach den Metazeichen *, ?, ( und [. Jedes, eines dieser Zeichen enthaltende Token wird als Muster eines Dateinamens interpretiert und durch die alphabetisch sortierte Liste der übereinstimmenden Dateinamen ersetzt. Expandiert ein solches Token nicht, erscheint es unverändert auf der Kommandozeile, falls die Shelloption nullglob nicht gesetzt ist. Anderenfalls wird das Token gelöscht. Die Metazeichen bedeuten im Einzelnen: * Beliebig viele beliebige Zeichen (auch 0)
? Genau ein beliebiges Zeichen
[...]
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). Und letztlich können die Zeichenklassen alnum alpha ascii blank cntrl digit graph lower print punct space upper xdigit verwendet werden ("[:digit:]"). Zeichen(...) Die in den Klammern eingeschlossenen Muster werden nur betrachtet, wenn die Shelloption extglob gesetzt ist. Die Muster sind eine Liste von Zeichenketten, die durch | getrennt sind. Das Zeichen vor der öffnenden Klammer reguliert die Auswertung des Musters:
ProzesssubstitutionDie 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.
Mit Hilfe der Prozesssubstitution könnte man den vi dazu bewegen, die Ausgaben eines Kommandos zu lesen:
Ein weiteres Beispiel dient zur Bestandsaufnahme laufender Prozesse:
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:
Innerhalb der Klammern >( ... ), <( ... ) können Parameter- Kommando- sowie arithmetische Substitutionen benutzt werden. QuotingDas Quoting wird verwendet, um die Interpretation von Sonderzeichen durch die Bash zu verhindern. Die Sonderzeichen sind ;, &, ( ), { }, [ ], |, >, <, Zeilenumbruch, Tabulator, Leerzeichen, $, ? und *. Ein einzelnes Sonderzeichen wird am einfachsten durch einen vorangestellten Backslash "gequotet". Die Bash betrachtet die Kombination aus Backslash und dem nachfolgenden Zeichen als ein einzelnes Zeichen, versucht aber nicht, dieses zu expandieren. Um mehrere Zeichen vor der Interpretation zu schützen, können diese zwischen zwei Anführungsstrichen (doppelt/einfach) eingeschlossen werden. Während die einfachen Anführungsstriche die Interpretation aller eingeschlossenen Sonderzeichen verhindern, erlauben doppelte Anführungsstriche die Substitution von Variablen und Kommandos. Ein kurzes Beispiel demonstriert den Sachverhalt:
TildeexpansionBeginnt der Wert mit einer ungequoteten 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:
WortzerlegungIn 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 "gequotet" wurden.
Die InitialisierungsdateienHandelt 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:
Gerade in Umgebungen, wo die 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, ...). Für gewöhnlich werden die Benutzer bestenfalls eine existierende Datei nach ihren Bedürfnissen anpassen, anstatt sich das notwendige Wissen anzueignen, um eine Konfigurationsdatei vollständig zu erstellen. Die nachfolgend, reichlich kommentierte Datei kann als Ausgangspunkt für eigene Experimente dienen:
Abhängig von der konkreten Linuxdistribution werden ggf. aus den beschriebenen Standarddateien heraus weitere Startup-Dateien eingelesen. Die ShellvariablenVariablen 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. Wichtige Variablen, die die Bash setzt, sind (Auswahl): PPID Prozessnummer des Bash-Vorgänger-Prozesses
PWD Aktuelles Arbeitsverzeichnis.
OLDPWD Vorhergehendes Arbeitsverzeichnis; bspw. könnten Sie mit "cd -" in dieses wechseln.
REPLY Variable, die die zuletzt vom Kommando read gelesene Zeile enthält.
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
RANDOM Erzeugt eine Zufallszahl bei jedem Zugriff.
OPTARG und OPTIND Wert und Index des letzten, von getopts bearbeiteten Argument.
HOSTNAME Rechnername ohne Domain.
HOSTTYPE Architekturtyp des Rechners
OSTYPE Typ des Betriebssystems.
SHELLOPTS Liste der Shelloptionen.
Wichtige Variablen, die die Bash benutzt, sind (Auswahl): IFS
Der Internal Field Separator enthält die Trennzeichen, anhand derer
die Bash nach erfolgter Expansion die Eingabe in Worte
zerlegt.
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.
HOME Enthält das Heimverzeichnis des Benutzers.
BASH_ENV Enthält eine Initialisierungsdatei, die beim Start von Subshells für Skripte aufgerufen wird.
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.
PS1...PS2 HISTSIZE Anzahl maximal gespeicherter Kommandos im History-Speicher
HISTFILE Datei, wo die History bei Ende einer interaktiven Bash gespeichert wird. Wie viele Zeilen diese enthalten kann, steht in HISTFILESIZE.
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.
Variablen besitzen einen Sichtbarkeitsbereich und lassen sich in lokale und globale Variablen unterteilen. 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.
Eine letzte Herangehensweise in die Thematik der Variablen betrachtet deren Eigenschaften, ob sie bspw. änderbar sind oder einen bestimmten Typ besitzen (vergleiche Variablen). Spezielle ShellvariablenNeben den soeben vorgestellten Variablen verfügt die Bash über zwei weitere Arten. Im Sprachgebrauch der Bash benennt man diese Positions- und spezielle Parameter. Beiden ist eigen, dass man ihre Werte zwar auslesen kann, aber die unmittelbare Zuweisung derselben nicht möglich ist. Die meisten Programme sind über Kommandozeilenoptionen steuerbar, so auch die Bash. Die Positionsparameter dienen nun dazu, auf diese Optionen zuzugreifen. Die Nummerierung erfolgt gemäß der Reihenfolge der Angabe der Parameter (die Token der Kommandozeile); die Zählung beginnt bei 0. Und da i.d.R. als erstes der Kommandoname in der Eingabe erscheint, verbirgt sich dieser hinter der Variable $0:
Wer sich so seine Gedanken über den Sinn der Positionsparameter macht, wird schnell auf deren Verwendung in Shellskripten schließen. Und genau dort werden wir intensiven Gebrauch davon machen. Wer weiter den Hirnschmalz strapaziert, wird sich fragen, wie man die Anzahl der Positionsparameter ermittelt. Auch diese verrät eine spezielle Variable $#; der Parameter $0 wird hierbei nicht mit gezählt. Die eigentlichen Positionsparameter verbergen sich in den Variablen $1,..., $9. Ein kurzes, nicht gerade geistreiches Beispiel soll die Verwendung andeuten; zum Erzeugen einer Parameterliste bemühen wir das eingebaute Kommando set:
Sie verstehen das Beispiel nicht? Dann schauen Sie sich bitte nochmals die Abschnitte zur Kommandosubstitution, zu Schleifen und Berechnungen an. Der Denker erkennt ein Problem: "Kann ich nur 9 Parameter an ein Shellskript übergeben?" Nein! Aber der Zugriff über die Positionsparameter ist nur auf die ersten 9 Einträge möglich. Benötige ich mehr, hilft das eingebaute Kommando shift weiter, das bei jedem Aufruf die Positionsparameterliste um eine (oder die angegebene Anzahl) Position(en) nach rechts verschiebt, d.h. nach dem Aufruf von "shift" steht in "$1" der Wert, der vor dem Aufruf in "$2" stand; in "$2" steht der aus "$3" usw. Der ursprüngliche Inhalt von "$1" ist ein für allemal verloren, es sei denn, man hat ihn sich in einer Variablen gemerkt. Wir kommen nochmals auf das letzte Beispiel zurück und berechnen nun die Summe der Zahlen 1, 2, ..., 100:
Im Zusammenhang mit den Positionsparametern stehen $* und $@, die auf den ersten Blick genau das gleiche Ergebnis erzielen, nämlich die gesamte Parameterliste zu expandieren, wobei die einzelnen Einträge jeweils durch den ersten Feldtrenner aus der Variablen IFS abgegrenzt sind. Der erste Parameter "$*" liefert dabei genau ein Wort zurück, während der zweite jeden Eintrag als eigenes Wort erzeugt. Das Ganze klingt mächtig verworren, aber zwei Anwendungsgebiete offenbaren den eigentlichen Nutzen. Zum einen wünscht man, eine Kommandozeile nach einem bestimmten Muster zu scannen. Jeden Parameter in einer Schleife einzeln zu durchleuchten, ist der Pfad des Sisyphus; hingegen den Inhalt von "$*" bzw. "$@" zu durchmustern, das elegante Vorgehen des aufmerksamen Lesers:
Die zweite (mir bekannte) Anwendung ist bei der Speicherung der Positionsparameter in einer Feldvariablen. Somit kann ohne Verwendung von shift auf alle Positionen zugegriffen werden.
Da IFS als erstes das Leerzeichen enthielt, »zerfällt« »$*« in einzelne Token und verhält sich analog zu »$@«. Quotet man beide Parameter in doppelte Anführungsstriche, offenbart sich der kleine aber feine Unterschied:
Die speziellen Parameter lassen sich nicht so säuberlich in eine Schublade pressen - zu unterschiedlich sind die ihnen angedachten Aufgaben: $? Diese Variable beinhaltet den Rückgabewert des zuletzt beendeten Vordergrundprozesses:
$! Enthält die Prozessnummer PID des zuletzt im Hintergrund gestarteten (und noch immer aktiven) Prozesses:
$- Beinhaltet alle durch das eingebaute Kommando gesetzten Shellvariablen (den symbolischen Buchstaben, nicht den Variablennamen selbst!):
Aktiv sind im Beispiel: hashall, interactive, monitor, braceexpand, histexpand. $_ In Abhängigkeit vom letzten Vorgang steht entweder das letzte Argument des zuletzt ausgeführten Shellskripts (oder der zuletzt gerufenen Shell) drin oder, wenn die Mailboxen auf neue Nachrichten überprüft werden, der Name der aktuell betrachteten Datei.
Die PromptsArbeitet 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:
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. Zunächst schauen wir uns den üblichen Inhalt der Variablen an:
"$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:
Vielleicht wäre etwas Farbe für das Prompt noch besser? Mit Escape-Sequenzen lässt sich auch dieses realisieren:
Die Erklärung und weitere Informationen zum Umgang mit Farben für die Konsole findet der interessierte Leser im Anhang Skriptsammlung. Konfiguration der Bash mit set und shoptsset 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. Beginnen wir mit set und betrachten zunächst den Status der von einer typischen Bash gesetzten Variablen:
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.
Viele Variablen deuten ihren Zweck bereits durch ihren Namen an. Die nützlichsten Vertreter möchten wir Ihnen nicht vorenthalten: 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:
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.
shopt ist ab der Bash-Version 2.0 neu hinzugekommen. Es behandelt weitere Shellvariablen und kennt (leider) abweichende Optionen. So verhilft "-p" zu einem Blick auf den Status der von "shopt" verwalteten Variablen:
Um eine Shellvariable zu aktivieren, ist die Option -s zu verwenden. Dem entgegen schaltet -u die Wirkung der Variablen ab. Wiederum betrachten wir nur eine Auswahl der Variablen im Detail: cdspell Ist die Option gesetzt, vermag die Shell geringe Tippfehler in Pfadangaben zu korrigieren:
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:
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:
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. AliasseZur Abkürzung immer wiederkehrender Kommandofolgen lassen sich für diese so genannte Aliasse definieren. Ein Alias wird mit dem Kommando alias erzeugt:
Ein Alias wird genauso benutzt wie das entsprechende Kommando:
Ein Alias existiert bis zum Ende der Shell, in der er definiert wurde oder bis zum expliziten Löschen mittels unalias:
Ein Aufruf von alias ohne Argumente bewirkt eine Auflistung aller definierten Abkürzungen. FunktionenEine 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:
Bei der Verwendung von Funktionen sind einige Regeln zu befolgen:
Einer Funktion können Parameter als Argumente übergeben werden:
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:
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. Eingebaute KommandosEs 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:
...nichts...? 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:
Betrachten wir die einzelnen builtin-Kommandos: : 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:
. <Datei> bzw. source <Datei> 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):
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.
bg Listet die Jobnummern aller Hintergrundprozesse auf.
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.
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:
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:
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 [Ctrl]+[b] an die Ausgabe des Kalenders des aktuellen Monats:
Ihre gesammelten Werke der Tastenbindungen können Sie auch in eine beliebige Datei schreiben und diese mittels "bind -f <Datei>" laden. 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:
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.
cd Zum Wechsel des aktuellen Verzeichnisses; Details stehen unter
NutzerkommandosDateien und Verzeichnisse.
command Kommando
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.
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:
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:
Abgesehen von -r und -r verwendet compgen dieselben Optionen wie das nachfolgend beschriebene complete. 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:
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 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:
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:
-f Dateinamenserweiterung
-u Benutzernamen Als Beispiel soll das Kommando mail mit einem Benutzernamen expandiert werden. Als Suffix wird automatisch "@outside.all" ergänzt:
-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
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
declare bzw. typeset
Zum Deklarieren von Variablen, wobei diese gleichzeitig mit Attributen versehen
werden können; siehe unter Variablen
dirs
Dient zur Anzeige des Verzeichnisstacks. Ein 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.
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. 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:
Das nächste Beispiel verwendet "\b", um den Cursor um eine Position zurück zu bewegen:
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.
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:
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:
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:
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:
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:
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:
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.
Beendet die Shell (das Shellskript) mit dem angegebenem Rückgabewert. Wird
kein Wert angegeben, liefert exit den Status des letzten Kommandos.
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:
Siehe unter Manipulation der History-Einträge
fg [Job] Holt den angegebenen ("Jobnummer") bzw. den zuletzt gestarteten Hintergrundprozess in den Vordergrund:
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:
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. 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:
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:
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. help
Schreibt einen kurzen Hilfetext zu einem eingebauten Kommando aus. Ein Beispiel
hierzu steht in Erste Schritte - Hilfe.
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:
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. Weitere Informationen zum Kommandozeilenspeicher findet man im gleichnamigen Abschnitt weiter unten. 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.
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.
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:
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:
logout
Beendet eine Login-Bash und meldet den Benutzer ab. In einer Nicht-Login-Bash hagelt es eine Fehlermeldung.
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«.
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:
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:
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:
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:
Ist die Anzahl erwarteter Token nicht vorhersehbar, bietet sich die Verwendung einer Feldvariablen an. Hierzu ist dem Variablennamen einzig die Option »-a« voranzustellen:
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:
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.
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.
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.
shopt
Dient zum Setzen bash-interner Variablen, die das Verhalten der Shell
maßgeblich beeinflussen. Nähere Informationen wurden bereits weiter oben gegeben.
suspend Suspendiert die Shell. Sie kann nur durch ein Signal SIGCONT reaktiviert werden.
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:
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):
-d Test auf Verzeichnis:
-e Existenz der Datei
-f Test auf normale Datei:
-k Test, ob das »sticky«-Flag auf ein Verzeichnis gesetzt ist:
-p Test auf Pipe:
-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:
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):
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):
Anmerkung: Die Prüfung und Beendigung eines Skripts im Fehlerfall lässt sich eleganter über die Parametersubstitution realisieren:
Mehrere Tests können kombiniert werden: ! Negation
-a Logisches UND zweier Tests
-o Logisches ODER zweier Tests
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):
trap Viele Programme lassen sich während der Laufzeit über bestimmte Signale beenden oder unterbrechen (bspw. schießt [Ctrl]+[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:
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 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. 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:
Etwas bedeckt hält sich type -t, was die Art des Arguments mit einem knappen Wort erklärt:
-a lässt das Kommando alle Interpretationen ausgeben und ist bei der Suche nach einem Kommando sicher eine nette Hilfe:
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:
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:
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 liegenden 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). 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:
Weitere Beispiele finden Sie im Abschnitt Zugriffsrechte. unalias Löscht den angegebenen Alias bzw. mit der Option -a alle Aliasse.
unset Dient zum Löschen einer Variable oder Funktion.
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«.
Editieren der KommandozeileDa sitzen Sie als Computerbändiger tagaus tagein vor der Klotze und dennoch trippeln ihre Finger bei weitem nicht so geübt über die Tastatur, wie eine erfahrene Sekretärin es vermag. Steigern Sie das Tempo, so schleichen sich ungewollte Fehler ein und die ganze Arbeit beginnt mit einem Kontrolllauf von vorn. Ja die Eingabefehler, die bleiben nicht aus. Doch im Unterschied zur altehrwürdigen mechanischen Schreibmaschine bietet der Rechner endlose Chancen der Fehlerbereinigung. Die Fülle der Navigationskommandos der Bash, um sich geschwind dem Fehler in der Eingabe anzunähern, sind so mannigfaltig, dass 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. Beginnen wir mit den Tastenkombinationen zum Positionieren des Cursors: [Ctrl]+[a] bzw. [Ctrl]+[e] Bewegt den Cursor an den Beginn bzw. das Ende der Zeile
[Ctrl]+[f] bzw. [Ctrl]+[b] Bewegt den Cursor ein Zeichen vor bzw. zurück
[Alt]+[f] bzw. [Alt]+[b] Bewegt den Cursor ein Wort vor bzw. zurück
[Ctrl]+[l] Löscht den Bildschirm und schreibt die aktuelle Zeile erneut
Text kann gelöscht, eingefügt oder auf den gelöschten Text (in einem Zwischenpuffer) zugegriffen werden: [Ctrl]+[d] Löscht das Zeichen unter dem Cursor
[Ctrl]+[k] Löscht den Text ab der Cursorposition bis zum Zeilenende
[Ctrl]+[u] Löscht den Text links des Cursors bis zum Zeilenanfang
[Alt]+[d] Löscht ab dem Cursor bis zum Ende des Wortes
[Ctrl]+[y] Fügt den zuletzt gelöschten Text an der Cursorposition ein
[Alt]+[y] Dieses Kommando kann nur unmittelbar nach [Ctrl]+[y] gerufen werden und rotiert den (Ring)Puffer der gelöschten Texte, dazu ein Beispiel:
Anmerkung: Der letzte Einfügevorgang funktioniert mit der angegebenen Tastenfolge nur, wenn einzig »Löschen« und »Einfügen« im Ringpuffer enthalten sind. Ein paar Tastenkombinationen mögen dem Einen nützlich und dem Anderen widersinnig erscheinen...: [Alt]+[u] bzw. [Alt]+[l] Wandelt das Wort ab Cursorposition in Groß- bzw. Kleinbuchstaben um
[Ctrl]+[u] Macht die letzte Änderung rückgängig
[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. Und schon von diesen werden Sie sich die wenigsten verinnerlichen, oder?. Sind Sie an einer vollständigen Auflistung interessiert, so konsultieren sie das eingebaute Kommando »bind -P«. Automatische VervollständigungEine 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. Ein Beispiel: Um den Inhalt der Datei »/usr/doc/packages/quota/README« zu betrachten, geben Sie folgende Zeile ein:
Sie mussten 35 mal die Tastatur bemühen... Versuchen Sie nun die folgende Tastenfolge:
Mit etwas Glück steht nun - nach nur 16 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:
Kann die Zuordnung nicht vollzogen werden, müssen Sie weitere Zeichen eingeben und anschließend die [Tab]-Taste erneut bemühen:
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ä
[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:
Umgang mit ProzessenIn Multitasking-Systemen arbeiten eine Menge von Prozessen quasi simultan. Etliche dieser Prozesse werden bereits beim Booten des Systems gestartet; ihrer Tätigkeit wird man auf einem schnellen Rechner kaum mehr gewahr. Für gewöhnlich ist an einen Rechner nur ein Terminal - eine Einheit aus Ein- und Ausgabegerät - angeschlossen, somit kann (oder besser sollte) nur ein Prozess Eingaben vom Terminal empfangen und Ausgaben auf dieses schreiben. Der Benutzer kann nun entscheiden, ob er einen mit dem Terminal verbundenen Prozess startet oder jenen von jeglicher Ein- und Ausgabe entkoppelt. In letzterem Fall spricht man von einem Hintergrundprozess. Mit dem Hintergrundprozess ist keinerlei Interaktion möglich, es sei denn, er wird mittels Signalbehandlung manipuliert oder ihm wird nachträglich das Terminal zugeteilt. Das Management von Prozessen bezeichnet man auch als Job-Kontrolle und alle modernen Shells bieten verschiedenen Mechanismen zu deren Unterstützung an. Vordergrund- und HintergrundprozesseRollen wir das Feld von hinten auf und beschreiben zunächst den Start eines Kommandos im Hintergrund. Ein simples Nachstellen des &-Zeichens genügt, um ein Kommando von der Standardeingabe zu entkoppeln und somit die Shell für neue Eingaben empfänglich zu machen:
Ein Kommando wie less arbeitet bis zum expliziten Abbruch durch den Benutzer. Von dieser Interaktion ist es jedoch durch den Start im Hintergrund abgeschnitten, sodass in diesem Fall auch die Ausgabe des Kommandos unterdrückt wird. Würde man less bspw. durch cat ersetzen, würde der Inhalt der Datei bla.txt dennoch angezeigt werden, da cat seine Arbeit beenden und damit den Ausgabepuffer leeren würde. Ist ein Kommando im Hintergrund wegen einer notwendigen Interaktion blockiert, vermag es erst weiter zu arbeiten, wenn die erwartete Eingabe erfolgte. Hierzu bedarf es jedoch der Standardeingabe. Anders gesagt, das Kommando muss in den Vordergrund geholt werden. Erreicht wird dies mit Hilfe von fg:
Im Beispiel wurde fg ohne die Angabe einer Jobnummer verwendet, es bringt daher den aktuellen Job (i.A. der zuletzt gestartete und noch aktive Hintergrundprozess) zum Vorschein. Um die weiteren Zugriffmethoden zu demonstrieren, starten wir drei Prozesse im Hintergrund:
Das Kommando jobs zeigt eine Liste der derzeitigen Hintergrundprozesse (der Shell!) an. Verwenden Sie die Option -l, um zusätzlich die Prozessnummern zu sehen bzw. -p, wenn Sie sich einzig für die PIDs interessieren. Der Zugriff auf den Job mit "sleep 200" kann nun über dessen Jobnummer oder den Kommandoaufruf erfolgen:
Anstatt der Angabe des kompletten Kommandoaufrufs kann ein eindeutiger Bestandteil der Aufrufzeile zur Selektion dienen. Im Beispiel identifiziert "2" die Zeile "sleep 200" eindeutig, so dass Angaben wie fg %?2, fg %20 oder auch fg %200 ein und denselben Job in den Vordergrund bringen. Des Weiteren holt fg %% bzw. fg %+ den zuletzt gestarteten Hintergrundprozess zurück auf die Konsole; fg %-1 beschreibt den vorletzten, fg %-2 den drittletzten,... gestarteten Hintergrundprozess. Was nun kein Hintergrundprozess ist, muss ein Vordergrundprozess sein. Zu einem Zeitpunkt kann es nur einen im Vordergrund aktiven Prozess geben. Es ist genau der, der die Eingabe weiterer Kommandos an die Shell verhindert. Einen solchen Prozess können Sie auch nachträglich in den Hintergrund befördern. Hierzu muss er zunächst gestoppt werden, indem ihm das Signal SIGSTOP gesendet wird. In der Bash übernimmt die Tastenkombination [Ctrl]-[z] diese Aufgabe:
Um einen solchen gestoppten Prozess nun im Hintergrund weiter laufen zu lassen, bemüht man das Kommando bg. Analog zu fg ist der zuletzt gestoppte Prozess gemeint, wenn dem Kommando kein weiteres Argument folgt. Auch die Auswahl aus mehreren angehaltenen Prozessen folgt der im Zusammenhang mit fg beschriebenen Syntax. Achtung! Die recht leichtfertige Sprachwahl vom "zuletzt gestoppten" oder "zuletzt im Hintergrund gestarteten" Prozess kann leicht verwirren, wenn sowohl Hintergrund- als auch angehaltene Prozesse koexistieren. In einem solchen Fall werden die gestoppten Prozesse VOR den Hintergrundprozessen eingereiht, d.h. die Kommandos greifen immer auf den zuletzt gestoppten Prozess zu!
Ist das System überlastet?Tummeln sich mehrere Benutzer zeitgleich auf einem Rechner und starten auch noch rechenintensive Programme, so erreichen CPU und/oder der Hauptspeicher bald ihre Grenzen. Das drastischste Mittel dem Andrang Einhalt zu gebieten, ist das Stoppen (bei reinem Mangel an CPU-Leistung) oder gar Beenden (wenn der Speicher erschöpft ist) einiger - für das System nicht zwingend notwendiger - Prozesse. Mit den bislang besprochenen Möglichkeiten sollten Sie in der Lage sein, den aktuellen Vordergrund abzuschießen oder zu stoppen. Hintergrundprozesse könnten Sie erreichen, indem Sie sie in den Vordergrund holen und wie gehabt verfahren. Allerdings fehlt Ihnen weiterhin der Zugriff auf Prozesse, die nicht die aktuelle Shell als Vorfahren besitzen. Signale sind die Nachrichten, die ein jeder Prozess versteht. Zahlreiche Signale besitzen vordefinierte Bedeutungen (siehe Allgemeines zu Shells, Prozesskommunikation), jedoch darf ein Programm diese ignorieren oder zu seinen Gunsten interpretieren. Das Signal SIGKILL (Nummer 9) führt stets zum Abbruch des betreffenden Prozesses (tatsächlich erreicht das Signal SIGKILL niemals den Zielprozess; der Kernel entfernt den Prozess einfach - sofern die Rechte stimmen). Im Zusammenhang mit der Beeinflussung der aktiver Prozesse ist die Kenntnis dreier Signale nützlich:
In der Bash existiert das eingebaute Kommando kill mit dem Signale an beliebige Prozesse gesendet werden können. kill erfordert die Angabe der Prozess- oder Jobnummern der betreffenden Prozesse sowie des zu sendenden Signals. Letzteres darf beim Signal SIGTERM entfallen, da dies die Voreinstellung ist.
Natürlich bedarf es zum Senden von Signalen an einen Prozess auch der Befugnisse:
Die Prozessnummern erfahren Sie bspw. mittels dem Kommando ps; Jobnummern mit jobs. An dieser Stelle sei noch erwähnt, dass ein Prozess nicht zwingend beendet werden muss, um seinen Rechenzeitverbrauch zu beschränken. Die Priorität eines Prozesses ist für die Zuteilung an CPU-Zyklen Ausschlag gebend, diese Kennzahl eines Prozesses kann sowohl bei dessen Start gesetzt, als auch zur Laufzeit modifiziert werden (nice bzw. renice). Da dies aber keine Eigenschaft der Bash ist, verweisen wir auf den Abschnitt Prozesssteuerung (Nutzerkommandos). Verzögerte AusgabeProzesse, die ihren Status wechseln, quittieren dies mit einer Ausgabe. Wie mit jeder Ausgabe verfahren wird, ist selbstverständlich konfigurierbar. Das typische Verhalten der Bash ist die Benachrichtigung über den Statuswechsel unmittelbar vor der Anzeige eines neuen Prompts:
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:
Der KommandozeilenspeicherDer Kommandozeilenspeicher - kurz History - ist eine Liste, die die zuvor eingegebenen Kommandozeilen enthält. 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. Um die nachfolgenden Ausführungen nachvollziehen zu können, sollten folgende Shellvariablen aktiv sein:
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. History-VariablenNeben den eingangs beschriebenen Variablen (die allesamt mit set oder shopt zu (de)aktivieren sind) wird der Kommandozeilenspeicher durch weitere Variablen beeinflusst: 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.
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". 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:
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").
HISTFILE
Diese Variable enthält den vollständigen Namen der Datei, in der die
History zu speichern ist. Sie sollte immer gesetzt sein.
HISTFILESIZE
Wie viele Zeilen maximal die unter HISTFILE benannte Datei enthalten darf,
wird hiermit fest gelegt. 500 ist ein typischer Wert.
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.
Zugriff auf die Einträge des KommandozeilenspeichersDie einfachste Methode des Zugriff ist die Verwendung vordefinierter Tastenkombinationen: bzw. Zugriff auf das vorherige/nächste Element der History-Liste.
[Ctrl]+[P] bzw. [Ctrl]+[N] Wie oben.
[Ctrl]+[R] Suche rückwärts nach einer Zeile, die mit dem eingegeben Muster übereinstimmt:
Durch wiederholte Eingabe von [Ctrl]+[R] wird die nächste Zeile geliefert, auf die das Muster zutrifft. [Ctrl]+[S] Suche vorwärts; Anwendung analog zu [Ctrl]+[R].
[Alt]+[<] bzw. [Alt]+[>] Erstes bzw. letztes Element der History-Liste (der Zugriff auf das letzte Element ist selten konfiguriert)
Zugriff über die History-SubstitutionZwei Zeichen leiten eine Sonderform des Zugriff auf den Kommandozeilenspeicher ein. Dabei handelt es sich - sofern die Variable "histchars" nichts Gegenteiliges behauptet - um das Ausrufezeichen "!" und das Dach "^". Leitet das Ausrufezeichen eine Kommandozeile ein und folgt diesem kein Whitespace, Punkt "." oder "=", so wird eine History-Substitution versucht. Folgt dem Ausrufezeichen eine Zahl, so wird die Kommandozeile aus dem Kommandozeilenspeicher entnommen und gestartet, die durch diese Zahl referenziert wird:
Neben diesem absoluten Zugriff kann auch auf einen konkreten Eintrag ausgehend vom aktuellen Index verwiesen werden. Um n-letzte Kommando erneut auszuführen, tippt man ein Minus vor die Zahl:
!! ist ein "abkürzende" Schreibweise für "!-1" (immerhin erspart das ein Zeichen). Des Weiteren ist der Zugriff auf die letzte Kommandozeile, die ein bestimmtes (festes) Muster enthält, möglich. Die letzte mit einem Muster beginnende Zeile wird durch !muster hervor gerufen; eine das Muster enthaltende Zeile bringt !?muster? zum Vorschein:
Mit dem Ausrufezeichen ist sogar der Zugriff auf die einzelnen Argumente des letzten Kommandos möglich: !:Argumentnummer. Um bspw. den Typ des letzten Kommandos fest zu stellen (der Kommandoname ist das 0. Argument), hilft folgende Eingabe:
Das letzte Argument ist mittels "!:$" abrufbar. Damit nicht genug... der Ausdruck !* bzw. !:* bietet Zugriff auf die gesamte Liste der Argumente des zuletzt ausgeführten Kommandos:
Letztlich handelt es sich im letzten Beispiel nur um einen Spezialfall der Bereichsangabe, die mit !:n-m die Argumente n, n+1, ..., m zurück holt:
Als Krönung des Ganzen lassen sich die vorgestellten Substitutionen kombinieren, so dass Sie auch Zugriff auf die Argumente früherer Kommandoaufrufe erlangen:
In vielen Situationen wird man eine Kommandozeile mit einem veränderten Argument erneut aufrufen. Anstatt die Kommandozeile zu editieren, kann die letzte Kommandozeile ausgeführt werden, wobei ein Teil dieser ersetzt wird. Man erreicht dies durch Substitution mittels ^Altes_Muster^Neues_Muster^:
Manipulation der History-Einträge mit fcDie bisherige Handhabung des Kommandozeilenspeichers zielte auf den Zugriff eines konkreten Eintrages ab, um diesen, ggf. nach vorheriger Manipulation auszuführen. Es konnte zu einem Zeitpunkt genau eine Kommandozeile editiert werden, wobei die Bearbeitung mit den Mitteln der Bash erfolgte. Eine Alternative, mit der auch mehrere Kommandos aus der History mit einem beliebigen Editor bearbeitet werden können, bietet das eingebaute Kommando fc. Nach Beendigung des Editors werden das/die Kommando(s) gestartet (eine Ausnahme bildet die Option -l). Um welchen Editor es sich handelt, kann durch die Variable FCEDIT fest gelegt werden. Ist sie nicht oder falsch belegt, so wird die Variable EDITOR konsultiert. Verweist auch deren Inhalt auf keinen existierenden Editor, wird der vi gestartet. Eine Angabe eines Editors per Kommandozeilenoption (-e Editor) genießt Vorrang vor den obigen Editor-Angaben. Die Option -l schreibt die Ausgabe von fc auf die Standardausgabe. Es ist der einzige Fall, in dem die Kommandos nicht ausgeführt werden. Um bspw. die letzten acht Kommandos in der History zu betrachten, hilft folgender Aufruf:
Die Nummerierung entspricht dem Eintrag eines Kommandos im Kommandozeilenspeicher und kann durch Angabe der Option -n unterdrückt werden. Bezug nehmend auf diese Nummerierung kann auch ein gezielter Bereich aus der History herausgezogen werden, indem dieser als "von bis" angegeben wird. Wir verzichten nachfolgend auf die Umleitung zur Standardausgabe, womit die gefundenen Zeilen einem Editor zugeführt werden:
Sie können die Kommandos nachfolgend editieren oder Löschen. Mit Abspeichern des Resultats wird versucht, jede der Zeilen auszuführen. Möchten Sie also keines der Kommandos starten, dann entfernen Sie sie aus dem Editor. Der Kommandozeilenspeicher bleibt davon unberührt. Aber wer merkt sich schon die Zahlen? Intuitiver wäre da eine Angabe der Art: "Alle Kommando zwischen der Kommandozeile, die mit jenem Muster begann und jener, die mit diesem Muster startete.". Auch das ist möglich:
Die Angabe zu bearbeitender Bereiche kann demnach anhand der Nummer des Historyeintrags oder anhand von Mustern, mit denen eine Kommandozeile begann, erfolgen. Ebenso ist die Kombination aus Muster und Nummer erlaubt. Um auf das n-letzte Kommando zuzugreifen, wird dessen Nummer mit einem vorangestellten Minus ("-5", "-100") angegeben. Die letzten drei Kommandos erreicht man nach diesem Schema mit:
Wird auf eine Bereichsangabe verzichtet, so zeigt fc -l die letzten 17 History-Einträge an. Bei der Arbeit mit der Bash geschieht es immer wieder, dass man eine Kommandozeile mehrfach auszuführen gedenkt, wobei bei jedem Durchlauf ein Muster durch ein anderes zu ersetzen ist. Findige Benutzer denken dabei gleich an eine "for"-Schleife über die erwünschten Muster, was sicherlich eine Lösung wäre. Das Kommando fc bietet in Kombination mit der Option -s einen Mechanismus, der genau dasselbe realisiert. Als Beispiel sollen 3 Dateien (chapter1, chapter2, chapter3) umbenannt werden. Für die erste Datei bemühen wir das Kommando mv. Für die folgenden nutzen wir die Substitution von Mustern durch das Kommando fc:
Bei solchen kurzen Kommandozeilen mag das manuelle Anpassen der Zeilen schneller vonstatten gehen, aber für komplexe Eingaben ist fc eine nützliche Hilfe. Im letzten Beispiel wurde kein Kommando angegeben, sodass fc die letzte Kommandozeile bearbeitete und startete. Auf einen zurückliegender Kommandoaufruf ließe sich durch Angabe eines eindeutigen Musters zugreifen:
Beachten Sie, dass das Kommando im Beispiel den letzten Aufruf von "cd" betrifft, unabhängig davon, ob in dessen Argumenten das Wort "share" auftauchte. Wird ein zu ersetzendes Muster nicht gefunden, wird nichts ersetzt; es ist aber kein Fehler! Speziell in der Kornshell wird häufig ein Alias r='fc -s' verwendet. Tippt man irgendwann r auf die Kommandozeile, so wird das letzte Kommando wiederholt ausgeführt. Gibt man jedoch bspw. r cd ein, so wird die letzte mit "cd" beginnende Kommandozeile gestartet. Möglich ist dies, da die Musterersetzung entfallen darf. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Korrekturen, Hinweise? |