This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
(quit) abbrechen. In der Anzeige ist die Spalte S wie Status interessant, die Auskunft über den Zustand der Prozesse gibt. Hierin bedeuten: • • • • •
R Running, Prozess ist bereit zu laufen und läuft, sobald ihm CPU-Zeit zugewiesen wird, S Sleeping, Prozess schlummert, bis er wieder etwas zu tun bekommt (die Prozessliste ist der reinste Schlafsaal), Z Zombie, Prozess, der beendet ist, dessen Überreste aber noch nicht weggeräumt sind, sollte selten auftauchen, D Dead, Prozess im Tiefschlaf, der nicht unterbrochen werden kann, T Traced or Stopped, Prozess wird verfolgt oder ist durch Signal angehalten. Praktisch nur zu sehen, wenn man top mit der Option -i (inverse) aufruft.
2.6 Prozesse
45
Das Werkzeug htop erlaubt horizontales Scrollen und damit die Anzeige langer Prozessnamen. In grafischem Gewand kommt der GNOME System Monitor (Nachfolger von gtop) daher, zu starten über die Menüpunkte Anwendungen -> Systemwerkzeuge -> Systemüberwachung, siehe Abbildung 2.2. Von KDE stammt das ähnlich aussehende Werkzeug KDE System Guard (ksysguard). Weitere Werkzeuge zur Systemüberwachung sind Conky (anpassungsfähig, braucht X11, http:// conky.sf.net/), siehe Abbildung 2.3, und Saidar (anspruchslos, curses-basiert, http://www.i-scream.org/), siehe Abbildung 2.4. Den Bedarf eines Prozesses an Arbeitsspeicher ermittelt pmap, auf Wunsch recht detailliert.
Abb. 2.4: Screenshot des curses-basierten Saidar System Monitors
Prozesse lassen sich durch Signale beeinflussen. Root darf jedem Prozess etwas signalisieren, ein gewöhnlicher Benutzer nur seinen eigenen. Eine Tabelle der Signale findet sich im Anhang in Tabelle 22.1 auf Seite 1063. Der Aufruf: joe@debian:~$ kill SIGTERM 3388 oder: joe@debian:~$ kill -15 3388 schickt dem Prozess Nr. 3388 das Signal Nr. 15 mit der Bezeichnung SIGTERM, das einen Prozess geordnet terminiert (beendet). kill ist auch ein eingebautes
46
2 Linux/UNIX
Kommando der Bash und genießt als solches Vorrang vor dem externen Kommando /bin/kill. Die Aufgaben sind gleich, die Syntax unterscheidet sich leicht. Weiteres in Abschnitt 2.7.3 Beenden von Prozessen auf Seite 71. Gleichzeitig auf einem System aktive Prozesse reden miteinander. Dafür gibt es mehrere Wege von gemeinsam genutztem Arbeitsspeicher bis hin zum Versand von Nachrichten. Die Mechanismen werden unter dem Begriff Inter Process Communication zusammengefasst, ein Thema hauptsächlich für Programmierer.
2.7 Kommandointerpreter (Bash) 2.7.1 Start Die Bash ist der gebräuchliche Kommandointerpreter (Shell) unter Debian GNU/Linux. Er ist die Schnittstelle zwischen Benutzer und Betriebssystem, der Gesprächspartner beim Terminaldialog. Die Bash nimmt Kommandos entgegen und lässt sie durch das Betriebssystem ausführen. Ferner stellt die Bash eine eigene Programmiersprache dar, mit der es möglich ist, Vorgänge zu automatisieren und sich Schreibarbeit zu sparen. Von dieser Eigenschaft wird an vielen Stellen im System Gebrauch gemacht. Beispielsweise handelt es sich bei den Startskripten im Verzeichnis /etc/init.d um Shellskripte. Man kann dort die gleichen Kommandos einfügen, die man auch an der Kommandozeile eingeben würde. Beide Aufgaben gehören nicht notwendigerweise zusammen. Während es für die Bash als Kommandointerpreter nur andere Shells als Alternative gibt, stehen zahlreiche Interpreter für Programmiersprachen zur Verfügung, die keine Kommandointerpreter sind. Der Shell als Programmiersprache ähnlich ist Perl, ebenfalls eine Skriptsprache, die von einem Perl-Interpreter in Maschinensprache übersetzt wird. Der Name Bash steht für Bourne-Again-Shell, eine Anspielung auf die BourneShell und ihren Autor S TEPHEN R. B OURNE. Diese Shell wurde 1977 erstmals von AT&T mit AT&T-Unix V7 ausgeliefert und ist heute auf vielen Linux/UNIXSystemen zu finden. Die Bash orientiert sich zwar an der Bourne-Shell und ist mit dieser verträglich, hat aber viele Eigenschaften aus anderen Shells – hauptsächlich der C-Shell und der Korn-Shell – übernommen, sodass mit ihr heute eine mächtige und komfortabel zu benutzende Shell zur Verfügung steht. Das Konzept des Kommandointerpreters ist nicht auf Linux/UNIX beschränkt. Von der Arbeit mit DOS, MS Windows oder IBM OS/2 kennen Sie vielleicht deren Kommandointerpreter command.com beziehungsweise cmd.exe. Auch die Aufgabe dieser Interpreter ist es, Kommandos entgegen zu nehmen und sie entweder selbst auszuführen oder die zugehörigen Programme aufzurufen. Ebenso ist es möglich, einfache Aufgaben mittels Batchdateien zu automatisieren. Es bestehen jedoch große Unterschiede bezüglich des Komforts und den Fähigkeiten zwischen diesen einfachen Kommandointerpretern und der Bash. Viele Aufgaben lassen sich schneller und einfacher durch Kommandos in der Kommandozeile lösen als durch Klicken, manche sogar ausschließlich. Man braucht nicht zu warten, bis aufwendige grafische Oberflächen gestartet sind, und spart sich
2.7 Kommandointerpreter (Bash)
47
die Hangelei von einem Menü zum anderen. Wenn man bedenkt, dass man diese Kommandos ebenso gut mit einem Texteditor in eine Datei schreiben kann und dann schon ein kleines Programm – ein Shellskript – hat, mit dem man die betreffende Aufgabe in Zukunft durch ein einziges Kommando erledigt, wird klar, welche Vorteile ein leistungsfähiger Kommandointerpreter gegenüber grafischen Oberflächen mit Menüs und Knöpfen bietet. Shellskripte werden auch Kommandoprozeduren oder Makrobefehle genannt, in der DOS-Welt Batch-Datei. Einen Einstieg in das Schreiben von Shellskripts vermitteln der Bash Guide for Beginners (2006) von M ACHTELT G ARRELS und der Advanced Bash Scripting Guide (2006) von M EN DEL C OOPER , beide beim Linux Documentation Project. Ein Beispiel am Anfang Stellen Sie sich folgende Aufgabe vor: Sie erhalten immer wieder Bilddateien in einem speziellen Format – sagen wir JPEG – die Sie drucken möchten und hinterher in einem anderen Format – beispielsweise PNG – speichern. Jede Datei soll zusätzlich auf eine Diskette geschrieben werden, damit Sie sie weitergeben können. Die übliche Vorgehensweise mit typischen Programmen für grafische Oberflächen ist: 1. 2. 3. 4. 5.
Öffnen eines Bildbearbeitungsprogramms aus der Startleiste. Wählen des Menüs Datei und Anklicken des Menüpunktes Öffnen. Warten, bis die Datei geladen ist. Nun wählen Sie wieder das Menü Datei und den Menüpunkt Speichern unter. Es erscheint eine Dateiauswahlbox, in der Sie einen neuen Dateinamen eingeben und den Dateityp (PNG) auswählen. 6. Danach drücken Sie ok, werden gefragt, ob Sie es wirklich ernst meinen, bestätigen und warten, bis die Datei konvertiert und gespeichert ist. 7. Nun wählen Sie Datei und Drucken, um die Datei zu drucken. Unter Umständen müssen Sie noch den Drucker angeben und einige Werte einstellen. 8. Um die Datei auf eine Diskette zu schreiben, wählen Sie entweder wieder Datei und Speichern unter und wählen diesmal das Diskettenlaufwerk aus, oder Sie kopieren die Datei mit einem Dateimanager auf eine Diskette. Wenn Sie und Ihre Programme schnell sind, brauchen Sie dafür jedes Mal drei Minuten. Und bedenken Sie auch, dass Sie sich bei einigen dieser Operationen konzentrieren müssen, damit Sie nicht einen falschen Dateinamen, ein falsches Verzeichnis, den falschen Drucker oder das falsche Dateiformat auswählen. Über die Kommandozeile dauert es am Anfang genauso lange, und Sie müssen sich auch konzentrieren. Hier gehen Sie folgendermaßen vor: 1. Sie wechseln in das Verzeichnis, in dem Ihre Dateien liegen: joe@debian:~$ cd grafiken 2. Dann konvertieren Sie die Datei mit dem Programm convert aus dem Paket imagemagick: joe@debian:~/grafiken$ convert bild.jpg bild.png
48
2 Linux/UNIX
3. Nun drucken Sie die Datei auf Ihren Farbdrucker mit dem Namen color: joe@debian:~/grafiken$ lpr -P color bild.png 4. und kopieren die Datei auf eine DOS-formatierte Diskette (siehe mtools): joe@debian:~/grafiken$ mcopy bild.png a: Das waren vier Kommandos. Weil Sie wissen, dass Sie die gleiche Aufgabe morgen wieder zu erledigen haben, schreiben Sie die Kommandos in eine Datei: #!/bin/bash cd grafiken convert bild.jpg bild.png lpr -P color bild.png mcopy bild.png a: Quelle 2.1 : Shellskript converter.sh
Das sind dieselben vier Kommandos wie oben. Eine solche Datei wird Shellskript genannt. Die erste Zeile enthält eine Besonderheit. Das Betriebssystem wird mit dieser Zeile angewiesen, den Übersetzer (Interpreter) /bin/bash zu verwenden, um das Programm auszuführen. So soll in jedem Skript für die Bash die erste Zeile aussehen. Die Zeichenfolge #! am Anfang der ersten Zeile wird Shebang genannt und muss genau dort stehen. Der Pfad des Übersetzers folgt unmittelbar. Sagen wir, Sie haben das Skript in der Datei converter.sh gespeichert. Um es ausführen zu können, sind noch die Rechte zu setzen: joe@debian:~$ chmod a+x converter.sh oder mit anderer, nicht völlig gleichbedeutender Schreibweise der Zugriffsrechte: joe@debian:~$ chmod 755 converter.sh Damit räumen Sie allen Benutzern das Recht ein, die Datei als Programm auszuführen. Nun wollen Sie nicht jeden Tag dieselbe Datei konvertieren, Variable kommen ins Spiel. Die Bash speichert in der Positionsvaraiblen mit dem Namen 1 das erste Argument, das ihr beim Aufruf übergeben wird. In der Variablen mit dem Namen 2 wird das zweite Argument gespeichert usw. Variable werden an Stelle von Zeichenketten benutzt. Ihnen wird ein Dollarzeichen vorangestellt, wenn sie bei der Ausführung des Skripts durch ihren Wert ersetzt werden sollen. Das verbesserte Skript: #!/bin/bash cd grafiken convert $1 $2 lpr -P color $2 mcopy $2 a: Quelle 2.2 : Verbessertes Shellskript converter2.sh
Nun rufen Sie Ihr Skript folgendermaßen auf:
2.7 Kommandointerpreter (Bash)
49
joe@debian:~$ ./converter2.sh Eingabedatei Ausgabedatei Punkt und Schrägstrich zu Beginn der Eingabe verlangen, das Kommando oder die Datei converter2.sh im Arbeitsverzeichnis zu suchen, nicht in dem durch die PATH-Variable festgelegten Befehlspfad. Für Eingabedatei setzen Sie den Namen der zu konvertierenden und zu druckenden Datei ein und für Ausgabedatei den Namen, den die Datei nach der Konvertierung tragen soll. Wenn Sie am nächsten Tag die gleiche Aufgabe mit der Datei baum.jpg wieder durchführen, rufen Sie nur noch folgendes Kommando auf: joe@debian:~$ ./converter2.sh baum.jpg baum.png Das geht schneller und erfordert weniger Mühe als die Durchführung verschiedener Menüoperationen mit der Maus oder das Eintippen mehrerer Kommandos. Auch wenn Sie hauptsächlich mit grafischen Benutzeroberflächen arbeiten, lohnt es sich, die wichtigsten Fähigkeiten der Bash kennen zu lernen. Viele Aufgaben lassen sich mit ihr einfach und zeitsparend bewältigen. Bei der Arbeit des Systemverwalters hat man es des öfteren mit Kommandos und Shellskripten zu tun, für deren Verständnis Bash-Kenntnisse erforderlich sind. Grundlegende Kenntnisse in der Kommandosprache der Bash sind bei einer über normale Anwendungen hinausgehenden Anpassung und Verwaltung eines Debian GNU/Linux-Systems unerlässlich, weil große Teile des Systems von Shellskripten konfiguriert und gesteuert werden. Aufrufen und Beenden der Bash Nach der Anmeldung an einem Terminal – beispielsweise an einer virtuellen Konsole – wird die Standardshell des Benutzers gestartet. Das ist fast immer die Bash gemäß dem Eintrag in der Datei /etc/passwd. Die Standardshell lässt sich mit dem Kommando chsh ändern. Wenn Sie sich an einer virtuellen Konsole anmelden, steht Ihnen die Bash sofort zur Verfügung. Etwas anders sieht es aus, wenn Sie mit dem X Window System (X11) arbeiten. Hier gibt es zunächst kein Terminal, in dem kommandozeilenorientierte Programme ausgeführt und benutzt werden. Dafür sind aber Terminal-Emulationsprogramme verfügbar, die die Funktionalität eines Terminals in einem X-Fenster bieten. Diese Programme starten in der Regel die Standardshell5 , sodass nach dem Aufruf eines Terminalprogramms in einem X-Fenster genauso wie an der Konsole gearbeitet werden kann. Das klassische Terminalprogramm für X11 ist das Programm xterm. Es stehen jedoch Alternativen wie rxvt oder die von den Arbeitsumgebungen KDE und GNOME bereitgestellten Programme konsole und gnome-terminal zur Verfügung. Die beiden Programme weisen den Vorteil auf, dass sie sich gut an die jeweiligen Arbeitsumgebungen anpassen. Ein Xterm rufen Sie über das Menü des von Ihnen benutzten Fenster-Managers oder über das Debian-Menü XShells -> Xterm auf. Das Programm konsole erreichen Sie unter KDE aus dem Panel unter Werkzeuge -> Konsole, und unter GNOME können Sie das Programm GNOME-Terminal im Panel unter Anwendungen -> Systemwerkzeuge -> Terminal finden. Einen Eindruck vermittelt Abbildung 2.5. Vielfach sind auch abgekürzte Wege zu den Terminalprogrammen eingerichtet. 5
Genauer das mit der Umgebungsvariablen SHELL eingestellte Programm.
50
2 Linux/UNIX
Abb. 2.5: Screenshot des Terminal-Emulators GNOME-Terminal. Kurz zuvor wurde das Kommando date eingegeben.
Die Bash wird durch das Kommando exit beendet. Sie beendet sich selbst, wenn der Datenstrom, aus dem sie liest (bei einer interaktiven Shell die Standardeingabe), zu Ende ist. Dies ist dann von Bedeutung, wenn die Shell nicht interaktiv betrieben wird, sondern die auszuführenden Kommandos aus einer Datei liest. Auch die Eingabe der Tastenkombination+ beendet bei entsprechender Konfiguration eine Shell. Beim Arbeiten unter einer grafischen Benutzeroberfläche findet sich im Menü Datei unten ein Punkt Fenster schließen oder ein Knöpfchen mit einem X in der rechten oberen Ecke des Fensters für denselben Zweck. Interaktive versus nicht-interaktive Shell Wenn die Bash nach der Anmeldung oder später per Kommando aufgerufen wird, startet sie als interaktive Shell. Sie nimmt Kommandos von der Tastatur entgegen und schreibt Mitteilungen oder Ausgaben von Kommandos auf den Bildschirm. Wird sie jedoch indirekt gestartet, um beispielsweise ein Skript auszuführen, liest sie ihre Kommandos aus der entsprechenden Skriptdatei. Grundsätzlich haben alle Kommandos unter beiden Bedingungen die gleiche Wirkung. Im interaktiven Modus verhält sich die Bash in einigen Situationen jedoch anders. So beendet sich beispielsweise eine nicht-interaktive Bash bei einem falsch eingegebenen Kommando nach Ausga-
2.7 Kommandointerpreter (Bash)
51
be einer Fehlermeldung sofort. Dies ist im interaktiven Modus unerwünscht. Hier wird ebenfalls eine Fehlermeldung ausgegeben, die Bash jedoch nicht beendet. Konfiguration und Startdateien Die Bash wird über mehrere Dateien konfiguriert. Dabei sind zwei Fälle zu unterscheiden. Im ersten Fall wird die Shell wie ein normales Programm aufgerufen. Das ist dann gegeben, wenn Sie am Prompt das Kommando bash eintippen oder – wie oben beschrieben – ein Terminalprogramm starten. In diesem Fall liest die Bash während ihres Starts die Kommandos, die sich in der Datei .bashrc im HomeVerzeichnis des aufrufenden Benutzers befinden, und führt sie aus. Sie wird über die $HOME/.bashrc-Datei konfiguriert. Das Zeichenpaar rc, dem wir noch häufiger begegnen, bedeutet run command oder run control. Im zweiten Fall wird die Bash als erstes Programm nach der Anmeldung am System gestartet. Jetzt hat sie einige zusätzliche Aufgaben zu erfüllen, welche die gestartete Arbeitsumgebung konfigurieren. Zu diesen Aufgaben gehört das Festlegen des Suchpfades für Programme (siehe unten). Eine solche Shell wird deswegen auch Login-Shell genannt. Sie führt zunächst die Kommandos in der Datei /etc/profile aus. Dies ist eine Systemkonfigurationsdatei, mit der Root Einstellungen festlegt, die für alle Sitzungen aller Benutzer mit der Bash gelten. Damit jedoch die Benutzer persönliche Anpassungen vornehmen können, werden danach die Kommandos in der Datei .bash_profile im Home-Verzeichnis des Benutzers ausgeführt. Der Fall, dass der Verwalter systemweite Einstellungen im Verzeichnis /etc vornimmt, die ein Benutzer durch Dateien in seinem Home-Verzeichnis überschreiben oder ergänzen kann, ist gängige Praxis vieler Anwendungen. Die dritte Konfigurationsdatei betrifft die Bash nur mittelbar. Es ist die Datei .inputrc im Home-Verzeichnis des Benutzers bzw. die Datei /etc/inputrc. Sie bestimmt die Eigenschaften der Bibliothek libreadline, die von der Bash – aber auch von anderen Programmen – benutzt wird, um Kommandozeilen zu lesen. Darüber hinaus kennt die Bash einige Optionen, die ihr Verhalten beeinflussen. Die Optionen lassen sich auch dann noch verändern, wenn die Bash schon gestartet ist. Sie werden bei dem Kommando set erläutert. Die Dateien .inputrc und /etc/inputrc Mit der Datei .inputrc wird zum einen die Bibliothek libreadline an die eigenen Bedürfnisse angepasst, zum anderen können Tastaturkommandos mit Funktionen verbunden werden. Wie unter Linux/UNIX üblich, wird beim Start der Bash zunächst die Datei /etc/inputrc gelesen, in der sich systemweite Einstellungen für alle Benutzer befinden, und danach die Datei .inputrc im Home-Verzeichnis des aufrufenden Benutzers, mit der jeder Benutzer eigene Einstellungen vornehmen und systemweite Einstellungen überschreiben kann. Die Datei wird beim Start jeder Bash gelesen. Die Tastenkombinationen+<x> + veranlassen ein erneutes Lesen der Datei. Um Änderungen der Datei wirksam werden zu lassen, muss die Bash beendet und wieder gestartet oder obige Tastenkombination benutzt werden.
52
2 Linux/UNIX
Allgemeines Verhalten von libreadline Es gibt zwei Formen von Anweisungen in der Datei. Änderungen des Verhaltens von libreadline werden durch das Schlüsselwort set eingeleitet, dem eine Eigenschaftsbezeichnung folgt. Dann kommt ein Wert, den die Eigenschaft annehmen soll. Ein Beispiel: set visible-stats on Mit der Anweisung wird die Eigenschaft visible-stats auf den Wert on gesetzt. Die Anweisung bewirkt, dass bei der Ausgabe von Dateinamen durch angehängte Zeichen verdeutlicht wird, ob es sich um besondere Dateien handelt (Verzeichnisse, symbolische Links etc.). Die Liste der einstellbaren Eigenschaften findet sich in der Info-Dokumentation (siehe Abschnitt 5.3 GNU Info-System auf Seite 282) zur Bash unter dem Suchwort READLINE. Der Aufruf man readline oder info readline zeigt die Beschreibung der Standardfunktion readline, nicht das, was wir brauchen. Zu den Eigenschaften gehören: •
completion-query-items Legt fest, wie viele mögliche Vervollständigungen es geben muss, bevor nachgefragt wird, ob diese angezeigt werden sollen. Voreinstellung ist 100. Die Anweisung: set completion-query-items 250
•
• •
•
•
•
bewirkt, dass nach zweimaligem Betätigen der Tastealle Vervollständigungen sofort angezeigt werden, wenn es nicht mehr als 250 gibt. show-all-if-ambiguous Die Darstellung der möglichen Vervollständigungen erfolgt normalerweise erst dann, wenn die -Taste ein zweites Mal gedrückt wird. Wird die Einstellung auf on gesetzt, erfolgt die Darstellung sofort. Vorgabe ist off. disable-completion Schaltet die Vervollständigung aus, wenn der Wert auf on gestellt ist. Vorgabe ist off. convert-meta Bewirkt, dass Zeichen, die nicht im ASCII-Zeichensatz vorkommen, in eine Zeichenkombination bestehend aus E SC und dem Ausgangszeichen – mit dem höchsten Bit auf 0 gesetzt – umgewandelt werden. Die Einstellung ist als Vorgabe auf on gestellt und sollte abgeschaltet (off) werden, um deutsche Umlaute eingeben zu können. expand-tilde Falls auf on gestellt, wird die Tilde bei der Vervollständigung in das Home-Verzeichnis umgewandelt. Vorgabe ist off. Die Bedeutung der Tilde als Abkürzung für das Home-Verzeichnis bleibt davon unbeeinflusst; das ist eine Eigenschaft der Bash selbst. horizontal-scroll-mode Wenn eine eingegebene Zeile länger ist als eine Zeile des Bildschirms, wird die Eingabe nicht automatisch umgebrochen, sondern der Text nach links verschoben. Vorgabe ist off. input-meta Erlaubt Zeichen einzugeben, die nicht im ASCII-Zeichensatz vorkommen, auch wenn dies vom Terminal nicht unterstützt wird. Vorgabe ist on.
2.7 Kommandointerpreter (Bash)
•
53
output-meta Bewirkt, dass Zeichen, die nicht im ASCII-Zeichensatz vorkommen, ausgegeben und nicht durch eine Escape-Sequenz ersetzt werden. Vorgabe ist on.
Tastenkommandos Die zweite Form von Anweisungen betrifft das Verbinden von Tastenkommandos mit Aktionen (key bindings). Angenommen dass immer, wenn Sie den Buchstaben GroßR eingeben, der Text Das war ein großes R erscheinen soll, dann tragen Sie folgende Zeile in die Datei .inputrc ein: "R": "Das war ein grosses R" Die Eigenschaft lässt sich nutzen, indem Sie die Funktionstasten oder Tastenkombinationen mitoder mit Kommandos verbinden. Zur Definition von Tastenkombinationen mit sind dem jeweiligen Buchstaben die Zeichen \C- voranzustellen. Um eine Definition mit der -Taste zu erzeugen, ist die Zeichenkette \M- (Meta) zu verwenden. Die gleiche Definition mit sieht so aus: "\C-r": "Das war ein Control-r" Durch die libreadline werden Funktionen zur Verfügung gestellt, die der Bearbeitung von Kommandozeile oder Kommandogeschichte dienen, Die Funktionen sind per Voreinstellung bei vielen Tastenkombinationen mit der -Taste und bei einigen mit der linken -Taste verbunden. Die Voreinstellungen werden durch Anweisungen wie die oben erwähnten überschrieben. Die Liste der Funktionen von libreadline findet sich in der Info-Dokumentation zur Bash. Deshalb werden in der folgenden Tabelle nur die wichtigsten Funktionen erläutert: Tab. 2.1: Tastenbelegung der Bash mit libreadline Funktion Cursor forward-char backward-char forward-word backward-word begin-of-line end-of-line
Erläuterung
Bewegt Cursor ein Zeichen vorwärts Bewegt Cursor ein Zeichen zurück Bewegt Cursor ein Wort vorwärts Bewegt Cursor ein Wort zurück Bewegt Cursor an den Anfang der Zeile Bewegt Cursor an das
Belegung
S TRG - F, P FEIL R ECHTS S TRG - B, P FEIL L INKS A LT- F A LT- B S TRG - A Fortsetzung
54
2 Linux/UNIX
Tab. 2.1: (Fortsetzung) Funktion
Erläuterung
Belegung
accept-line
Ende der Zeile Abschicken der Kommandozeile
S TRG - E E INGABE
Text delete-char backward-delete-char kill-line backward-kill-line kill-word undo yank tab-insert transpose-words
Löscht Zeichen unter Cursor Löscht das Zeichen vor Cursor Löscht Text zwischen Cursor und Zeilenende Löscht Text zwischen Cursor und Zeilenanfang Löscht Zeichen zwischen Cursor und Wortende Macht jüngste Veränderung rückgängig Fügt zuletzt gelöschte Zeichen wieder ein Fügt Tabulatorzeichen ein Verschiebt Wort unter dem Cursor hinter nächstes Wort
Kommandogeschichte previous-history Geht ein Kommando zurück und zeigt es an next-history Geht ein Kommando vorwärts und zeigt es an backward-search-history Eingabe einer Zeichenkette, nach der die Kommandogeschichte durchsucht wird Vervollständigung complete insert-completions Sonstiges clear-screen
S TRG - D, E NTF Z URÜCK S TRG - K S TRG - X, Z URÜCK A LT- D S TRG -_ S TRG - Y A LT-TAB A LT- T
S TRG - P, P FEIL R AUF S TRG - N, P FEIL RUNTER
S TRG - R
Vervollständigt den eingegebenen Begriff Fügt alle Vervollständigungen ein
A LT-∗
Löschen des Bildschirminhalts
S TRG - L
TAB
Diese Funktionen sollten nicht anderen Tastenkommandos zugeordnet werden, weil sie auch in anderen Programmen, beispielsweise dem Editor Emacs, verwendet werden. Deswegen eignen sich Funktionstasten zur Definition eigener Kommandos und Makros. Leider sind die Funktionstasten nicht so einfach anzusprechen wie
2.7 Kommandointerpreter (Bash)
55
die übrigen. Dies liegt daran, dass unterschiedliche Terminaltypen unterschiedliche Zeichenfolgen bei Betätigen dieser Tasten erzeugen. Die meisten Terminaltypen erzeugen ein Escape-Zeichen (\e) und dann eine Zeichenfolge, die auf die Nummer der Funktionstaste schließen lässt. Wir geben deshalb hier eine Beispielkonfiguration an, die Funktionstastenbelegungen für die Terminaltypen linux (Konsole) und eine generische Belegung enthält: # Einstellungen wegen deutscher Sonderzeichen: set input-meta on set convert-meta off set output-meta on # Wir wollen nicht gefragt werden, wenn es # weniger als 200 Vervollstaendigungen gibt, # ob diese angezeigt werden sollen: set completion-query-items 200 # Ausserdem moechten wir sofort alle Vervoll# staendigungen sehen und nicht ein zweites Mal # TAB druecken muessen: set show-all-if-ambiguous on # Wir haetten gerne einen Hinweis, um was fuer # Dateitypen es sich bei Vervollstaendigungen # handelt: set visible-stats on # Die Tilde soll in das Home-Verzeichnis # uebersetzt werden: set expand-tilde on # Belegung der Tastenkombination CTRL-r: # Ein komplizierteres Tastaturmakro, das # uns einen Suchbefehl schreibt und den # Cursor an die Stelle bewegt, wo wir # etwas einsetzen muessen (einzeilig): "\C-r": "find /usr/share/doc -name \"*gz\" | xargs zgrep -i \"\" | less \M-b\M-b\C-f\C-f\C-f" # Belegungen Funktionstasten Linux-Konsole: $if term=linux "\e[[A": reverse-search-history # Funktion F1 "\e[[B": clear-screen # Funktion F2 "\e[[C": "Funktion F3"
56
2 Linux/UNIX
"\e[[D": "Funktion F4" "\e[[E": "Funktion F5" # Definition fuer andere Terminaltypen: $else "\e[11~": reverse-search-history # Funktion F1 "\e[12~": clear-screen # Funktion F2 "\e[13~": "Funktion F3" "\e[14~": "Funktion F4" "\e[15~": "Funktion F5" $endif # Diese Funktionstasten waren bei getesteten # Terminals gleich und brauchen nur einmal # angegeben zu werden: "\e[17~": "Funktion F6" "\e[18~": "Funktion F7" "\e[19~": "Funktion F8" "\e[20~": "Funktion F9" "\e[21~": "Funktion F10" "\e[23~": "Funktion F11" "\e[24~": "Funktion F12" Das Doppelkreuz (Raute, Nummernzeichen, Gartenzaun, E: number sign, hash mark, F: croisillon ) leitet einen Zeilenkommentar ein, der bis zum Zeilenende reicht. Bei einem mehrzeiligen Kommentar ist daher jede Zeile mit einem Doppelkreuz zu beginnen. Der Kommentar darf auch nach Anweisungen irgendwo auf der Zeile beginnen. Leere Zeilen werden überlesen. Makrodefinitionen stehen in Anführungszeichen. In der Beispieldatei .inputrc sind folgende Einstellungen vorgenommen worden: • •
•
•
Ganz oben finden sich die drei Einstellungen für readline, mit denen deutsche Sonderzeichen auf vielen Terminaltypen funktionieren sollten. Darunter finden sich zwei Einstellungen für die Vervollständigung. Zunächst wird festgelegt, dass bei weniger als 200 möglichen Vervollständigungen keine Nachfrage durchgeführt werden soll. Außerdem sollen die möglichen Vervollständigungen sofort angezeigt werden. Dann wird festgelegt, dass beim Anzeigen von Vervollständigungen der Typ der Dateien kenntlich gemacht werden soll und dass die Tilde während der Vervollständigung in den Namen des Home-Verzeichnisses umgewandelt werden soll. Darauf folgt die Definition eines Tastaturmakros. Der Tastenkombination+ wird ein Makro zugeordnet, das ein längliches Kommando zum Durchsuchen komprimierter Dateien im Dokumentationsverzeichnis /usr/share/doc erzeugt (siehe find, xargs und zgrep). Weil der Text, nach dem gesucht werden soll, bei jeder Benutzung des Makros ein anderer ist, ist er nicht enthalten. Vielmehr wird der Cursor gleich an die Stelle bewegt, an
2.7 Kommandointerpreter (Bash)
•
57
der der Text einzusetzen ist (zweimal ein Wort zurück (+) und drei Buchstaben vor mit dreimal + ). Man sieht, dass in Makros neben Zeichenketten auch alle Tastenbefehle vorkommen dürfen. Das Makro enthält Anführungszeichen. Weil Anführungszeichen in der Datei .inputrc selbst eine besondere Bedeutung haben, muss ihnen ein Gegenschrägstrich zwecks Quotens vorangestellt werden. In dem darunterliegenden Block finden sich Definitionen für Funktionstasten. Wir machen von der Möglichkeit Gebrauch, mit der Anweisung $if eine Bedingung – hier den Terminaltyp – abzufragen und abhängig davon unterschiedliche Definitionen vorzunehmen. Welche weiteren Bedingungen abgefragt werden können, ist in der Info-Dokumentation zur Bash erläutert. Weil sich bei den drei Terminaltypen nur die Zeichenfolgen der ersten fünf Funktionstasten unterscheiden, sind nur diese in die $if-, $else- und $endif-Anweisungen eingeschlossen. Die darauf folgenden Definitionen gelten für alle Terminaltypen. Sinnvoll belegt sind hier nur die Funktionstasten und . Auf der ersten liegt jetzt das Kommando zum Durchsuchen der Kommandogeschichte (reverse-search-history) und auf der zweiten die Funktion zum Löschen des Bildschirms. Alle anderen Funktionstasten sind mit Makros belegt, die lediglich den angegebenen Text ausgeben. Es bleibt dem Leser überlassen, sinnvolle Makros einzusetzen. Die Definitionen für die beiden ersten Funktionstasten sind nicht von Anführungszeichen umschlossen. Der Grund besteht darin, dass es sich hier um die Namen von Funktionen und nicht um auszugebende Zeichenketten handelt.
Die Datei /etc/profile Über die Datei /etc/profile werden Einstellungen vorgenommen, die für alle Benutzer gelten, deren Standardshell die Bash oder die Korn-Shell ist. In ihr finden sich gewöhnliche Shell-Kommandos, die während des Starts dieser beiden Shells ausgeführt werden. Um beispielsweise das Programm news jedesmal dann aufzurufen, wenn die Bash als Login-Shell gestartet wird, ist in die Datei eine Zeile mit dem Kommando news einzufügen. Die wichtigste Aufgabe der Datei besteht jedoch darin, sinnvolle Voreinstellungen festzulegen, mit denen Benutzer auch dann arbeiten können, wenn sie auf eigene Konfigurationsdateien verzichten. Dazu gehören das Festlegen der Verzeichnisse, in denen die Bash nach Programmen suchen soll (PATH-Variable), und Vorgaben für die Zugriffsrechte neuer Dateien (umask). Die Datei ~/.bash_profile Nachdem die Bash als Login-Shell die Kommandos in der Datei /etc/profile abgearbeitet hat, führt sie die Kommandos in der Datei .bash\_profile im Home-Verzeichnis des Benutzers aus. Hier hat jeder Benutzer die Möglichkeit, zusätzliche Einstellungen vorzunehmen oder Vorgaben aus /etc/profile zu überschreiben.
58
2 Linux/UNIX
Wenn der Systemverwalter das Kommando news nicht in die Datei /etc/ profile aufgenommen hat, aber man dennoch möchte, dass dieses Programm nach der Anmeldung aufgerufen wird, ist das Kommando in die eigene Konfigurationsdatei .bash_profile einzufügen. Auch möchte man eventuell den vorgegebenen Suchpfad für Programme erweitern. Die Datei $HOME/.bashrc Wenn die Bash nicht als Login-Shell gestartet wurde, führt sie lediglich die Kommandos in dieser Datei aus. Es wird dann davon ausgegangen, dass alle wichtigen Einstellungen und Umgebungsvariablen für die Sitzung bereits gesetzt worden sind und lediglich Einstellungen des Benutzers an der Shell selbst vorgenommen werden sollen. In der Praxis möchte man oft die gleichen Einstellungen für Login-Shell und normale Shell benutzen. Durch folgende Zeile in der Datei .bash_profile wird erreicht, dass die Shell als Login-Shell auch alle Kommandos in der Datei .bashrc ausführt: . ~/.bash_profile Der Punkt am Anfang, gefolgt von einem Leerzeichen, ist ein Shell-Kommando, das die Bash veranlasst, die in der angegebenen Datei enthaltenen Anweisungen auszuführen. Wenn Sie das Kommando in Ihre Datei .bash_profile aufnehmen, brauchen Sie alle weiteren Einstellungen nur noch in der Datei .bashrc vorzunehmen. Sie gelten dann automatisch für die Login-Shell und normale Shells. Trotzdem ist es weiterhin möglich, Kommandos, die nur für die Login-Shell gelten sollen, in die Datei .bash_profile zu schreiben. Die Datei ~/.bash_logout Ebenso wie beim Start der Bash Kommandos automatisch ausgeführt werden, können bestimmte Kommandos während der Beendigung der Bash aufgerufen werden. Hierzu dient die Datei .bash_logout im Home-Verzeichnis des Benutzers. Sie wird nur während der Beendigung einer Login-Shell abgearbeitet. Man könnte von einigen Dateien Sicherungskopien (Backups) anlegen lassen oder temporäre Verzeichnisse leeren. Größere Arbeiten verzögern die Abmeldung und passen nicht gut hierher. Hilfe und Dokumentation Die Bash ist mit einer Hilfefunktion ausgestattet, die eine Kurzreferenz zu allen eingebauten Kommandos liefert. Der Aufruf: joe@debian:~$ help | less
2.7 Kommandointerpreter (Bash)
59
gibt eine Liste der eingebauten Kommandos aus. Wenn dem help-Kommando der Name eines eingebauten Kommandos als Argument mitgegeben wird, erscheint eine Beschreibung des Kommandos. Folgendes Kommando liefert eine Beschreibung des Kommandos alias: joe@debian:~$ help alias Darüber hinaus gibt es Linux/UNIX-üblich eine Manual-Seite zur Bash: joe@debian:~$ man bash in der mit den vom Editor vi oder Pager less bekannten Kommandos vorwärts und rückwärts geblättert oder gesucht werden kann. Zum Ausdrucken dient folgendes Kommando: joe@debian:~$ man bash -T ps | lpr Der Ausdruck umfasst rund 70 Seiten DIN A4. Auch im GNU-Info-System ist die Bash dokumentiert: joe@debian:~$ info bash Je nach Einrichtung des Systems zeigt info gelegentlich auch nur das Manual an. Im Verzeichnis /usr/share/doc/bash liegen weitere Informationen. Schließlich kann man sich von http://www/tldp.org/ ein Bash-Prompt-HOWTO (2003) von G ILES O RR und einen Bash Guide for Beginners (2006) von M ACH TELT G ARRELS herunterladen. 2.7.2 Grundlagen der Bash Externe und interne Kommandos Die wichtigste Aufgabe der Shell besteht darin, Kommandos vom Benutzer entgegenzunehmen und auszuführen. Man unterscheidet dabei zwischen externen Kommandos und internen oder eingebauten Kommandos. Externe Kommandos sind Programme, die von der Shell aufgerufen werden, nachdem der Benutzer ein Kommando eingegeben hat. Diese haben normalerweise den gleichen Namen wie das Kommando, das eingegeben wurde. Beispiele solcher Programme sind die Kommandos ls oder rm. Im Gegensatz zu anderen Kommandointerpretern lässt die Bash fast alles, was sie nicht unbedingt selbst machen muss, von externen Programmen erledigen. Kommandos lassen sich dadurch leichter austauschen, und die Bash bleibt ein relativ schlankes Programm, das nicht allzuviel Speicher benötigt. Die Bash sucht nach externen Programmen in den Verzeichnissen, die in der Variablen PATH aufgeführt sind (siehe Abschnitt 2.7.5 Variable auf Seite 75). Interne oder eingebaute Kommandos werden von der Bash eigenhändig ausgeführt. Nachdem ein Kommando eingegeben wurde, wird überprüft, ob es sich um ein eingebautes handelt. Falls ja, führt die Shell es selbst aus. Wenn nicht, sieht sie nach,
60
2 Linux/UNIX
ob es ein Programm mit dem Namen des Kommandos gibt, und führt es aus. Interne Kommandos werden dort benötigt, wo die Eigenschaften des Prozesses der Bash selbst verändert werden sollen, beispielsweise beim Wechsel des Arbeitsverzeichnisses mittels cd oder beim Ändern von Umgebungsvariablen mittels export. Viele interne Kommandos dienen auch der Ablaufsteuerung von Shellskripten oder wurden eingebaut, weil sie schneller auszuführen sind als externe Programme. Manche Kommandos kommen sowohl als interne wie als externe Kommandos vor, beispielsweise echo. Eingebaute Kommandos haben keine eigene Manualseite, sondern werden auf der Manualseite der Shell erläutert. Ein weiteres Beispiel eines internen Kommandos ist alias. Es definiert neue Kommandos auf Basis bereits bekannter Kommandos. Angenommen, Sie wollten das Kommando ls immer mit der Option - -color=auto aufrufen, so geben Sie folgendes Kommando ein: joe@debian:~$ alias ls="ls - -color=auto" Das Kommando weist die Bash an, immer dann, wenn das Kommando ls eingegeben wird, das Kommando ls - -color=auto zu verwenden. Das originale Kommando ls erreichen Sie nur noch über seinen absoluten Pfad /bin/ls. Ein weiterer, oft benutzter Aliasname ist folgender: joe@debian:~$ alias ll="ls -l" oder noch kürzer: joe@debian:~$ alias l="ls -l" Auf diese Weise kann man seinen Lieblingskommandos kurze Namen verpassen, aber bitte vorher prüfen, ob der Aliasname nicht schon belegt ist. Ein Alias bleibt nur so lange gültig, wie die Bash läuft, in der er eingegeben wurde. Alias-Anweisungen, die immer gültig sein sollen, müssen deswegen in eine der Startdateien der Bash (gewöhnlich /etc/bash.bashrc oder \$HOME/.bashrc) geschrieben werden, damit sie jedesmal beim Start der Bash ausgeführt werden und für die gesamte Sitzung zur Verfügung stehen. Gibt man nach einem Alias in der Kommandozeile eine Option oder ein Argument an, so macht man sich die Wirkung klar, indem man das Alias buchstäblich durch seine Definition ersetzt. Ist das Alias eine Pipe, werden Option oder Argument dem letzten Glied übergeben. Das ist nicht immer das Gewollte. Benutzung Im einfachsten Fall wird der Name eines Programms eingegeben und dieTaste gedrückt, woraufhin die Bash das betreffende Programm startet. Weil viele Programme eine Reihe von Parametern erwarten, ist es in der Bash möglich, solche Parameter anzugeben. Sie werden durch ein oder mehrere Leerzeichen von dem Programmnamen und von anderen Parametern getrennt. Die Benutzung der Bash ist hängt hauptsächlich ab von der Konfiguration der Bibliothek libreadline, die auch die beiden im Folgenden erläuterten Eigenschaften zur Verfügung stellt. Die
2.7 Kommandointerpreter (Bash)
61
Bash bietet verschiedene Mechanismen zum Editieren der Kommandozeile. Einige diesbezügliche Tastenkommandos finden Sie in Tabelle 2.1 auf Seite 53. Vervollständigung Eine Funktion der Bibliothek ist die Kommandovervollständigung. Sie spart zum einen viel Tipparbeit und schützt zum anderen vor Schreibfehlern. Mit der Kommandovervollständigung reicht es, einige Anfangsbuchstaben eines Kommandos oder eines Dateinamens einzugeben. Wenn danach die Tastegedrückt wird, schaut die Bash nach, welche Kommandos oder Dateinamen mit diesen Buchstaben anfangen. Es gibt dann drei Möglichkeiten: 1. Nur ein einziges Kommando oder Argument fängt mit diesen Buchstaben an. In diesem Fall wird der Aufruf automatisch vervollständigt, sodass Sie nicht weiterzuschreiben brauchen. 2. Es gibt mehrere Kommandos oder Dateien, die mit den eingegebenen Zeichen anfangen. Je nach Einstellung durch die Datei .inputrc werden dann sofort oder nach einer zweiten Betätigung der -Taste die verfügbaren Kommandos oder Dateien angezeigt. Gewöhnlich reicht es in einem solchen Fall aus, wenige weitere Buchstaben einzugeben, bis das Kommando oder der Dateiname eindeutig ist, und dann wieder zu drücken, um das Wort zu vervollständigen. 3. Es gibt kein Kommando oder Dateinamen mit den eingegebenen Zeichen am Anfang. Ein Warnton erschallt. Legt man beispielweise bei einem Textprojekt zahlreiche Dateien in einem Verzeichnis an, dann sollen die Dateinamen aussagekräftig sein, was eine gewisse Länge erfordert. Achtet man darauf, dass sich alle Dateinamen schon in den ersten drei Zeichen unterscheiden, ist man dank der Vervollständigung des Eintippens langer Namen enthoben. Eine Alternative wäre das Anlegen kurzer Symlinks auf die langen Namen, aber das ist nicht so elegant und bläht das Verzeichnis auf. Die Bash erkennt, wann sie nach einem Programm- und wann nach einem Dateinamen suchen muss. Am Anfang einer Zeile steht immer ein Kommando. Als Argument werden Kommandos nur selten, Dateinamen aber oft übergeben. Neben Programm- und Dateinamen können auch Rechnernamen (nach einem @-Zeichen), Benutzernamen (nach einer Tilde) und Variablennamen (nach einem $-Zeichen) ergänzt werden. Die Schreibweise ./datei versteht die Vervollständigung nicht, das übersteigt ihren Horizont. Falls Sie nur die Anfangsbuchstaben einer Datei kennen, können Sie diese einfach eingeben, ein oder zweimal drücken, die möglichen Vervollständigungen betrachten und dann einige weitere Buchstaben eingeben und wieder drücken. Wenn Sie die Eingabe eines Kommandos oder eines Dateinamens immer mit der -Taste überprüfen, kommt es nicht vor, dass Sie eine Fehlermeldung aufgrund eines nicht gefundenen Kommandos oder einer nicht gefundenen Datei erhalten.
62
2 Linux/UNIX
Kommandogeschichte (History) Die Bash speichert die eingegebenen Kommandos in der Datei .bash\_history im Home-Verzeichnis. Auf diese Weise stehen sie auch dann noch zur Verfügung, wenn Sie sich zwischendurch abgemeldet oder den Rechner ausgeschaltet haben. Mit Hilfe der Datei lässt sich ermitteln, was Sie als letztes auf dem Rechner gemacht haben. Falls Sie aus Gründen der Sicherheit dieses Verhalten nicht wünschen, löschen Sie beim Abmelden die History, wozu sich die Datei .bash\_logout anbietet. Die wichtigsten Kommandos zur Benutzung der Kommandogeschichte sind die Pfeiltastenund <down>. Mittels erscheinen die zuletzt eingegebenen Kommandos, mit <down> geht es zurück in die Gegenwart. Die Kommandogeschichte lässt sich durchsuchen. Angenommen, Sie haben irgendwann folgendes Kommando eingegeben: joe@debian:~$ find / -xdev -nouser -print | less Hundert Kommandos später wollen Sie das Kommando erneut eingeben. Dazu benutzen Sie die Funktion backward-search-history der Bibliothek libreadline. Die Funktion ist als Vorgabe mit der Tastenkombination + verbunden; im Beispiel oben wurde sie auf die Funktionstaste gelegt. Sie betätigen die Taste, es erscheint folgende Zeile: (reverse-i-search)‘’: Nun fangen Sie an, Ihr Kommando wieder einzugeben. Mit jedem Zeichen erscheint das jüngste Kommando, das mit den bislang eingegebenen Zeichen anfängt, bis nach wenigen Eingaben das gewünschte Kommando gefunden ist: (reverse-i-search) ‘find /’: find / -xdev -nouser -print | less Dann drücken Sie nur noch , und das Kommando wird ausgeführt. Standardein- und -ausgabe, Standardfehlerausgabe, Umleitung Programme für grafische Benutzeroberflächen fragen den Benutzer oft, mit welcher Datei gearbeitet werden soll und in welche Datei die Arbeitsergebnisse geschrieben werden sollen. Dies beherrschen Programme für die Kommandozeile auch. Unter Linux und anderen Betriebssystemen stehen Programmen vom Anfang der Terminalsitzung an drei formale Dateien zur Verfügung, aus denen sie lesen bzw. in die sie schreiben, ohne sie eigens zu öffnen. Diese Dateien bezeichnet man als Standardeingabe (stdin, E: standard input, F: entrée standard), Standardausgabe (stdout, E: standard output, F: sortie standard) und Standardfehlerausgabe (stderr, E: standard error output, F: sortie standard d’erreur). Die Standardausgabe ist die Datei, in die Programme schreiben. Sie ist als Vorgabe mit dem Bildschirm des Terminals verbunden, von dem das Programm gestartet wird (Kontrollterminal). Wenn ein Programm auf die Standardausgabe schreibt, erscheint das Geschriebene auf dem
2.7 Kommandointerpreter (Bash)
63
Bildschirm. Dasselbe gilt für die Standardfehlerausgabe. Auch sie ist normalerweise mit dem Bildschirm verbunden. Zwischen den beiden Kanälen wird unterschieden, um Ergebnisse und Fehlermeldungen voneinander zu trennen. Tab. 2.2: Standarddateien Name
Datei
Standardeingabe Standardausgabe Standardfehlerausgabe
stdin stdout stderr
Deskriptor 0 1 2
Gerät Tastatur Bildschirm Bildschirm
Die Standardeingabe ist mit der Tastatur des Terminals verbunden, in dem das Programm läuft. Von ihr wird gelesen. Wenn ein Programm Daten liest oder Benutzerantworten entgegennimmt, dann macht es das über die Standardeingabe. Alles, was Sie in ein Terminal eingeben, wird von dem Programm gelesen, dessen Kontrollterminal es ist. Nach dem Start der Bash ist dies die Bash. Sie liest alle Eingaben von der Standardeingabe. Wie jede Datei kann auch die Standardeingabe ein Ende haben. Solange sie mit einem Terminal verbunden ist, beenden Sie die Standardeingabe, indem Sie die Tastenkombination+ drücken (üblich, aber mittels stty konfigurierbar). Andere Betriebssysteme kennen noch die Dateien stdaux (Standard Auxiliary Device) und stdprn (Standard Printer Device). Braucht Linux/UNIX nicht. Wenn ein Kommando ausgeführt wird, erscheint seine Ausgabe auf dem Bildschirm des Terminals, von dem er gestartet wurde. Manchmal möchte man die Ausgabe statt auf den Bildschirm in eine Datei schreiben. Nehmen wir an, Sie schreiben einen längeren Text. Sie möchten jeden Abend festhalten, wie groß Ihre Datei ist, um den Fortgang der Arbeit zu überwachen. Dazu dient das Kommando: joe@debian:~$ ls -l diplom Um zu ereichen, dass die Ausgabe von ls gleich in einer Datei landet, erweitern wir das Kommando: joe@debian:~$ ls -l diplom > fortschritt Das Zeichen > bewirkt, dass die Ausgabe statt auf den Bildschirm in die Datei fortschritt geschrieben wird. Die Datei wird jedesmal von vorn beschrieben, der vorhergehende Inhalt geht verloren. Da wir aber den Fortschritt über der Zeit verfolgen wollen, verwenden wir ein geringfügig anderes Kommando: joe@debian:~$ ls -l diplom > > fortschritt Das Zeichenpaar >> bewirkt, dass die Ausgabe des Kommandos an den bereits vorhandenen Inhalt der dahinter bezeichneten Datei angehängt wird. Sie wird nicht überschrieben, sondern fortgeschrieben. Wenn die Datei vorher noch nicht vorhanden war, wird sie auch mit diesem Kommando erzeugt. Der von der Shell geleistete Mechanismus wird Umleitung (E, F: redirection) genannt.
64
2 Linux/UNIX
Geöffnete Dateien werden außer über ihren Namen (Dateipointer) auch über einen Deskriptor (Index, Handle) angesprochen. Da es sich bei Standardein- und -ausgabe sowie bei der Standardfehlerausgabe um geöffnete Dateien handelt, ist dies auch hier der Fall. Der Deskriptor für die Standardeingabe hat die Nummer 0, der Deskriptor für die Standardausgabe die Nummer 1 und der für die Standardfehlerausgabe die Nummer 2. Für Programmierer: Systemaufrufe verwenden Deskriptoren, C-Standardfunktionen Dateipointer, das sind Pointer (Adressvariable) auf Strukturen vom Typ FILE. Vom Anwendungsprogramm geöffnete Dateien erhalten Deskriptoren von 3 an aufwärts. Die Deskriptoren lassen sich mit den Operatoren > und < mit anderen Dateien als dem Terminal verbinden. Obiges Kommando: joe@debian:~$ ls -l diplom > fortschritt ist insofern eine Abkürzung für das Kommando: joe@debian:~$ ls -l diplom 1> fortschritt weil hier der Deskriptor 1 (Standardausgabe) mit der Datei fortschritt verbunden wird. Ebenso funktioniert die Umleitung mit der Standardfehlerausgabe. Das Programm ls erzeugt eine Fehlermeldung, wenn es Daten über eine Datei anzeigen soll, die es nicht gibt. Die Fehlermeldung wird auf die Standardfehlerausgabe geschrieben. Vorausgesetzt die Datei titel existiert nicht, dann gibt das folgende Kommando nur eine Fehlermeldung aus: joe@debian:~$ ls -l diplom titel 1> fortschritt Die reguläre Ausgabe von ls wird wieder in die Datei fortschritt geschrieben, die Fehlermeldung erscheint auf dem Bildschirm. Wenn auch Fehlermeldungen in eine Datei umgeleitet werden sollen, ist folgendes Kommando einzugeben: joe@debian:~$ ls -l diplom titel 1> fortschritt 2> fehler Mit dem Kommando werden die Fehlermeldungen (Deskriptor 2, Standardfehlerausgabe) in die Datei fehler umgeleitet. Die Trennung zwischen Standardausgabe und Standardfehlerausgabe ist vor allem dann hilfreich, wenn die Ausgabe eines Programmes von anderen Programmen weiterverarbeitet werden soll. Die Ausgabe des obigen Kommandos könnte folgendermaßen aussehen: -rw-r--r--
1
joe
joe
431559
Jul 16 23:20
diplom
Weil Sie nur die Größe der Datei und nicht die anderen Daten protokollieren wollen, schneiden Sie diesen Wert mit dem Kommando cut aus: joe@debian:~$ cut -c 34-41 protokoll Dann leiten Sie die Ausgabe des Kommandos in eine neue Datei um:
2.7 Kommandointerpreter (Bash)
65
joe@debian:~$ cut -c 34-41 protokoll > groesse Was würde passieren, wenn in der Datei protokoll auch etwaige Fehlermeldungen enthalten wären? Die cut-Kommandos würden nicht die Dateigröße, sondern irgendeine sinnlose Zeichenkette aus der Fehlermeldung herausschneiden. Durch die Trennung der beiden Kanäle ist es auch möglich, nur die Fehler eines Programms zu beobachten. Ein Programm könnte jede Nacht prüfen, ob die Größen und Rechte wichtiger Dateien noch stimmen oder – vielleicht von einem Eindringling – geändert worden sind. Die Daten würde es dann in Protokolldateien schreiben, die normalerweise niemanden interessieren. Nur wenn es wirklich eine Abweichung findet, würde es eine Mitteilung auf die Standardfehlerausgabe geben, die man in eine Datei umleiten und an den Verwalter schicken könnte. Falls Standardausgabe und Standardfehlerausgabe doch zusammen in eine Datei umgeleitet werden sollen, dient hierzu der Operator &. Er verbindet zwei Deskriptoren miteinander. So führt das Kommando: joe@debian:~$ ls -l diplom titelblatt 1 > protokoll 2>&1 dazu, dass sowohl die Ausgabe des Kommandos ls als auch die Fehlermeldungen in der Datei protokoll gespeichert werden (Deskriptor 2 wird mit Deskriptor 1 verbunden). Das Kommando lässt sich abkürzen: joe@debian:~$ ls -l diplom titelblatt &> protokoll Gelegentlich bietet sich zur Ausgabeumleitung die Datei /dev/null an. Die Gerätedatei stellt den Mülleimer (bit bucket) des Systems dar, das Schwarze Loch im Datenraum. Alle Daten, die nach /dev/null geschrieben werden, gehen sofort unrettbar verloren. Möchte man die Fehlerausgabe eines Programms unterdrücken, leitet man die Standardfehlerausgabe auf /dev/null um. Dies geschieht in unserem Beispiel mit folgendem Kommando: joe@debian:~$ ls -l diplom titelblatt > protokoll 2 > /dev/null Lesen aus /dev/null liefert ein End-of-File-Zeichen (EOF) zurück. Etwas anders funktioniert die Umleitung der Standardeingabe. Hier wird der Operator < benutzt. Das Programm cut, das wir eben benutzt haben, liest normalerweise von der Standardeingabe, wenn es nicht den Namen einer Datei als Argument übergeben bekommt. So hätte man an Stelle von: joe@debian:~$ cut -c 34-41 protokoll > groesse auch Folgendes schreiben können: joe@debian:~$ cut -c 34-41 < protokoll > groesse Hierdurch wird die Standardeingabe aus der Datei protokoll gelesen, nicht von der Tastatur. Wird das Kommando ohne Umleitung der Standardeingabe eingegeben, so wartet es darauf, dass Eingaben vom Benutzer am Terminal vorgenommen werden. Diese werden mit der Tastenkombination+ (EOF) beendet. Wenn der letzte
66
2 Linux/UNIX
Teil (> groesse) weggelassen wird, erscheint das Ergebnis wieder auf der Standardausgabe. Here Documents Vorwiegend in Shellskripten möchte man Text oder Daten, die von einem Programm gelesen werden sollen, nicht aus einer Datei lesen lassen, sondern direkt im Skript oder in der Kommandozeile angeben. Diesem Zweck dient der Operator << Zeichenkette. Es werden dann alle nachfolgenden Zeilen als Standardeingabe benutzt, bis die mit Zeichenkette angegebene Zeichenkette als einzige in einer Zeile vorkommt: #!/bin/bash cat << EOF Das Skript gibt den hier angegebenen Text aus, bis die Zeichenkette EOF allein in einer Zeile steht. Es hätte auch jede andere Zeichenkette verwendet werden können. EOF wird aber oft benutzt, weil das für End-Of-File steht und die Zeichenkette das Ende von stdin anzeigt. EOF Hier liest das Programm cat den Text zwischen der zweiten Zeile und der EOF-Zeile und gibt ihn auf die Standardausgabe aus. Dieses Verfahren wird in Skripten oft benutzt, um längere Ausgaben, beispielsweise Erläuterungen oder Hilfetexte, einfach zu formulieren. Der von den beiden EOF-Marken eingerahmte Text wird als Here Document bezeichnet und kann mit anderem Inhalt auch von weiteren Programmen benutzt werden, die eine kurze, feststehende Eingabe von der Standardeingabe benötigen (URLs, Telefonnummern, Benutzernamen, nur ungern Passwörter in Klartext). Kommandoverkettung (Pipe) Im obigen Beispiel ist es lästig, dass zwei Kommandos hintereinander eingegeben werden müssen. Außerdem stört es, dass zwischendurch eine Datei angelegt wird, die keine interessierenden Daten enthält. Mit dem Operator | (senkrechter Strich, E: vertical bar, F: barre verticale) verbindet man die Standardausgabe eines Programms mit der Standardeingabe eines zweiten Programms, ohne Zwischendateien zu benutzen. Das sieht so aus: joe@debian:~$ ls -l diplom | cut -c 34-41 >> groesse Hier wird die Ausgabe des Kommandos ls -l mit der Eingabe des Kommandos cut verbunden, und die Ausgabe von cut wird an die Datei groesse angehängt. Eine solche Konstruktion bezeichnet man als Pipe (Röhre, Pipeline, Fließband, E: pipe, F: tube). Pipes können von Programmen ähnlich wie Dateien benutzt werden. Es werden auf der einen Seite Daten in sie hineingeschrieben und auf der anderen Seite
2.7 Kommandointerpreter (Bash)
67
aus ihnen gelesen. Mit dem Kommando mkfifo ist es sogar möglich, im Dateisystem Pipes mit eigenem Namen zu erzeugen, die man wie Dateien benutzen kann. Eine solche benannte Pipe ist nach Gebrauch ausdrücklich zu löschen. Die Verkettung von Kommandos ist ein wichtiges Instrument. So gibt es eine ganze Reihe kleiner, spezialisierter Werkzeuge, die erst dann sinnvoll genutzt werden können, wenn sie durch Pipes mit anderen Programmen verkettet werden. Mit solchen Pipes erhält man Kommandos, die auf die betreffende Aufgabenstellung zugeschnitten sind. Ein Beispiel dafür ist die Konvertierung von DOS-Textdateien für die Benutzung unter Linux/UNIX. Unter DOS und MS Windows werden Zeilenenden in Textdateien durch ein Wagenrücklaufzeichen (CR, Carriage Return) und ein Zeilenvorschub-Zeichen (LF, Line Feed) kodiert, wohingegen unter Linux/UNIX das Zeilenvorschub-Zeichen (LF) allein ein Zeilenende markiert. Um eine unter DOS erstellte Textdatei unter Linux/UNIX lesen zu können, müssen die CR-LF-Paare in LF-Zeichen umgewandelt werden. Dies geschieht mit dem Programm fromdos aus dem Debian-Paket sysutils. Weiter wird unter DOS oft ein anderer Zeichensatz benutzt als unter Linux/UNIX. Die Zeichensatztabelle beschreibt, welches Zeichen durch welche Zahl repräsentiert wird. Das führt dazu, dass beispielsweise deutsche Umlaute in solchen Dateien unter Linux/UNIX nicht zu lesen sind, wenn diese nicht an die richtige Zeichensatztabelle angepasst werden. Dies geschieht mit dem Programm recode. Angenommen, Sie haben die Textdatei diplom unter DOS erstellt und wollen Sie nun unter Linux weiterbearbeiten. Dann geben Sie folgendes Kommando ein, um gleichzeitig die Konvertierung der Zeilenenden und die Konvertierung von einer Zeichensatztabelle in eine andere durchzuführen: joe@debian:~$ cat diplom | fromdos | recode cp850/..latin1 > diplom_linux Das Kommando cat schreibt den Inhalt der Datei diplom auf die Standardausgabe, die mit der Standardeingabe des Kommandos fromdos verbunden wird. Das Programm konvertiert die Zeilenenden und gibt das Ergebnis wiederum auf die Standardausgabe, die von recode als Standardeingabe gelesen wird. Das Programm recode konvertiert nun die Zeichen in der Datei ausgehend von der DOSZeichensatztabelle (code page) cp850 in die unter Linux/UNIX übliche Zeichensatztabelle latin1. Die Standardausgabe von recode wird schließlich in die Datei diplom_linux umgeleitet, die hinterher die Linux-Version des Textes enthält. Wenn Sie die Datei später wieder unter DOS bearbeiten möchten, drehen Sie die Pipe um: joe@debian:~$ cat diplom_linux | recode latin1..cp850/ | todos > diplom_dos Hier wird das Kommando todos benutzt, um die Zeilenenden in das DOS-Format zu bringen6 . Pipes sind ein Weg der Inter Process Communication. 6
Die Dateikonvertierungen können auch von dem Programm recode allein geleistet werden. Sie dienen hier nur der Beschreibung von Pipes.
68
2 Linux/UNIX
2.7.3 Auftragsverwaltung (Jobverwaltung) und Prozessverwaltung Unter Linux/UNIX ist es möglich, zeitgleich mehrere Prozesse auszuführen (Multitasking). Diese Fähigkeit des Betriebssystems wird von der Bash auf vielfältige Weise unterstützt. Ein Prozess ist die Form, in der ein Programm ausgeführt wird. Er liegt im Arbeitsspeicher, besteht aus einer Kopie des auszuführenden Programms sowie Daten und fordert vom Zentralprozessor Rechenzeit an. Jeder Prozess auf einer Maschine hat eine eindeutige Nummer, die Prozess-ID, die man mittels ps erfährt. Nach dem Start der Bash sind Standardein- und -ausgabe der Bash mit dem Terminal verbunden, von dem sie gestartet wurde (Kontrollterminal). Alles, was Sie auf der Tastatur eingeben, geht an die Bash und alles, was die Bash ausgibt, geht auf den zugehörigen Bildschirm. Wenn Sie in der Bash ein anderes Programm aufrufen, dann werden Standardein- und -ausgabe mit diesem Programm verbunden. Angenommen, Sie rufen von der Bash aus den Texteditor vi auf. Jeder Tastenanschlag dient nun der Benutzung von vi und nicht mehr der Bash. Erst wenn der Editor beendet ist, kommt die Bash wieder an der Reihe. Der &-Operator Ein Beispiel: Sie möchten eine Datei anlegen, in der die Namen aller Dateien auf dem System stehen, die Ihnen gehören. Dies bewerkstelligen Sie mit dem Kommando find. Das Kommando: joe@debian:~$ find / -user kurt gibt die Namen aller Dateien auf dem System aus, die dem Benutzer kurt gehören. Das Kommando: joe@debian:~$ find / -user kurt &> kurts_dateien schreibt die normale Ausgabe sowie die Fehlerausgabe des Kommandos in die Datei kurts_dateien. Wenn Sie das Kommando ausprobieren, sehen und hören Sie, dass es dauert, bis das gesamte Dateisystem durchsucht worden ist. Währenddessen ist das Kontrollterminal blockiert, weil die Bash wartet, bis das Kommando beendet ist. Man sagt auch, der Prozess laufe im Vordergrund oder synchron ab. Durch den Operator & (et-Zeichen, E: ampersand , F: esperluette) ist es möglich, Prozesse in den Hintergrund zu stellen. Wenn Sie das obige Kommando folgendermaßen eingeben, können Sie sofort mit der Bash weiterarbeiten: joe@debian:~$ find / -user kurt &> kurts_dateien & Nach der Eingabe des Kommandos liefert die Bash zwei Zahlen zurück: [1] 3642 Die erste Zahl (in eckigen Klammern) ist die Auftrags- oder Jobnummer des Prozesses, den Sie in den Hintergrund gestellt haben. Die zweite Zahl ist die Prozessnummer des dazugehörigen Prozesses. Dazu muss man wissen, dass jeder Prozess
2.7 Kommandointerpreter (Bash)
69
vom System eine eindeutige Prozessnummer erhält, über die er angesprochen werden kann. Die Prozessnummer wird auch Prozess-ID (PID) genannt. Wenn ein im Hintergrund ablaufender Prozess beendet wird, teilt die Bash dies ebenfalls mit. Sie erhalten eine der folgenden Ausgaben: [1]- Done find / -user kurt &> kurts_dateien [1] Exit 1 find / -user kurt &> kurts_dateien [1]- Terminated find / -user kurt &> kurts_dateien [1]- Killed find / -user kurt &> kurts_dateien Im ersten Fall wurde der Prozess normal beendet (Done). Im zweiten Fall wurde der Prozess beendet und hat einen Rückgabewert geliefert, der von 0 abweicht. Damit zeigen Prozesse an, dass Fehler aufgetreten sind. Im Fall des find-Kommandos kann dies daran liegen, dass find versucht hat, Verzeichnisse zu durchsuchen, für die es keine Leseberechtigung hat. Die letzten beiden Ausgaben erscheinen, wenn der Prozess von außen durch ein Signal beendet wurde. Die Mitteilung Terminated bedeutet, dass ein anderer Prozess diesen Prozess aufgefordert hat, sich zu beenden, und Killed bedeutet, dass ein anderer Prozess diesen Prozess gewaltsam beendet hat, ohne ihm die Chance zu geben, sich selbst zu beenden (siehe kill). Solche Meldungen erscheinen erst dann, wenn die Eingabe einer Kommandozeile abgeschlossen ist. Der Grund ist, dass man beim Eingeben von Kommandos nicht gestört werden möchte. Wenn Sie wissen wollen, ob es irgendwelche Meldungen gab, schicken Sie einfach eine leere Kommandozeile ab, indem Sie die Tastedrücken. Umschalten zwischen Prozessen Gelegentlich möchte man einen Prozess zunächst im Vordergrund starten und dann für eine Weile in den Hintergrund stellen, um etwas anderes zu erledigen, ohne den Prozess zu beenden. Ein Beispiel dafür ist die Arbeit mit Editoren. Wenn Sie mit dem vi eine Datei bearbeiten und zwischendurch kurz etwas nachsehen wollen, müssten Sie Ihre Datei speichern, den Editor beenden, dann nachsehen, was Sie wissen wollen, und hinterher den Editor wieder starten. Beim Arbeiten in mehreren Fenstern oder mit mehreren Desktöppen geht das natürlich eleganter, aber wenn man nur ein Terminal hat, müsste man so vorgehen. Die großen Editoren kennen das Problem und bieten einen Weg, um vorübergehend eine Shell aufzurufen (aus-shellen), ohne den Editor zu unterbrechen. Mit der Tastenkombination + halten Sie einen Prozess, der im Vordergrund ausgeführt wird, an und stellen ihn in den Hintergrund (suspendieren). Sie erhalten den Prompt der Bash und können andere Dinge tun, bis Sie den Prozess wieder in den Vordergrund holen. Der Vorgang unterscheidet sich vom Starten eines Prozesses im Hintergrund dadurch, dass der betreffende Prozess durch + angehalten wird. Wenn Sie das oben aufgeführte Kommando nochmals ohne &Operator aufrufen:
70
2 Linux/UNIX
joe@debian:~$ find / -user kurt &> kurts_dateien und danach+ drücken, erhalten Sie folgende Meldung: [1]+ Stopped find / -user kurt &> Die Bash teilt mit, dass der Prozess angehalten ist. An der ausbleibenden Festplattenaktivität merken Sie auch, dass das Kommando nicht mehr ausgeführt wird. Es besteht nun die Möglichkeit, den Prozess entweder im Hintergrund wie mit & weiter auszuführen oder ihn wieder in den Vordergrund zu holen. Selbstverständlich kann er auch erstmal im Hintergrund verbleiben. Um einen Auftrag in den Hintergrund zu stellen, ist folgendes Kommando einzugeben: joe@debian:~$ bg [Jobnummer] Das Wort bg steht für Background. Die optional anzugebende Jobnummer bezeichnet die Auftragsnummer des Prozesses, der in den Hintergrund gestellt werden soll. Wird sie nicht angegeben, dann wird der zuletzt angehaltene Prozess in den Hintergrund gestellt. Im Beispiel ist folgendes Kommando einzugeben: joe@debian:~$ bg %1 Als Argument erwartet bg entweder eine Prozess-ID ohne oder eine Jobnummer mit vorangestelltem Prozentzeichen. Statt der Jobnummer darf der Jobname verwendet werden. Es erscheint eine Bestätigung, dass der Prozess im Hintergrund weiter ausgeführt wird: [1]+ find / -user kurt &> kurts_dateien & An der wieder einsetzenden Festplattenaktivität ist zu hören, dass der Prozess das Dateisystem weiter durchsucht. Mit dem Kommando fg (foreground) wird ein Prozess wieder in den Vordergrund geholt. Der Aufruf lautet: joe@debian:~$ fg [Jobnummer] Auch hier ist die Angabe der Jobnummer optional. Wird sie nicht angegeben, dann wird der zuletzt angehaltene oder in den Hintergrund gestellte Prozess in den Vordergrund geholt. Um den find-Prozess wieder in den Vordergrund zu bringen, ist folgendes Kommando einzugeben: joe@debian:~$ fg %1 Die Bash bestätigt das Kommando dadurch, dass sie das Kommando anzeigt, welches wieder im Vordergrund ausgeführt wird: find / -user kurt &> kurts_dateien Wird eine Datei mit dem Editor vi bearbeitet und soll der Editor kurzzeitig in den Hintergrund gestellt werden, um etwas anderes zu tun, so ist während der Arbeit + zu drücken, und es erscheint der Prompt der Shell. Dann können Sie andere Tätigkeiten durchführen, beispielsweise mit less der Inhalt einer anderen
2.7 Kommandointerpreter (Bash)
71
Datei betrachten. Ist man damit fertig, gibt man das Kommando fg ein und setzt die Arbeit mit dem vi fort. Das Kommando jobs zeigt an, welche Aufträge in dem betreffenden Terminal gerade ausgeführt werden und welche Jobnummern sie haben. Wenn Sie viele Aufträge im Hintergrund laufen haben und die Jobnummer eines Prozesses, den Sie in den Vordergrund schalten wollen, nicht mehr wissen, benutzen Sie dieses Kommando, um die Jobnummer zu ermitteln. Beenden von Prozessen Manchmal soll ein Prozess von außen beendet werden. Dafür kann es verschiedene Gründe geben. Man hat ein Kommando falsch geschrieben und möchte nicht abwarten, bis es vollständig ausgeführt ist, ein Programm tut etwas, was es nicht soll, und muss beendet werden, oder ein Programm soll neu gestartet werden, damit es seine Konfigurationsdateien erneut liest usw. Der Verwalter muss auch dann und wann einen Benutzerprozess beenden, der nicht freiwillig Feierabend macht. In der Regel können Prozesse, die im Vordergrund ausgeführt werden, durch die Tastenkombination+ gestoppt werden. Ob das betreffende Programm dann wirklich Schluss macht, hängt davon ab, wie es programmiert wurde. Das Beenden von Prozessen erfolgt durch Senden eines Signals an den Prozess. Programme entscheiden selbst, was zu tun ist, wenn sie ein bestimmtes Signal empfangen. Über diesen Mechanismus kann ein Programm, das ein Signal zu seiner Beendigung erhalten hat, noch aufräumen, beispielsweise Daten sichern oder temporäre Dateien löschen, bevor es sich verabschiedet. Eine Ausnahme stellt das Signal 9 (SIGKILL) dar, das von keinem Prozess abgefangen werden kann und sofort zur Beendigung des Prozesses führt. Sie sollten das Signal nur dann verwenden, wenn Sie einen Prozess beenden müssen, der sich widerborstig zeigt und auf keine andere Weise beenden lässt. Um ein Signal an einen Prozess zu senden, wird das eingebaute Kommando kill oder das externe Kommando /bin/kill benutzt. Das Kommando sendet als Vorgabe das Signal 15 (SIGTERM) an Prozesse. Das Signal führt – ähnlich wie das durch die Tastenkombination + erzeugte Signal 2 (SIGINT) – zur sanften Beendigung eines Prozesses. Die Syntax von kill lautet: kill [-Signal] Prozess-ID | %Jobnummer [Prozess-ID | %Jobnummer ...] Dem Kommando kann mit Signal die Nummer oder der Name eines Signals übergeben werden. Eine solche Angabe wird mit einem Minuszeichen eingeleitet. Anschließend wird ihm mit Prozess-ID oder Jobnummer die Prozess-ID oder Jobnummer des Prozesses übergeben, an den das Signal gesendet werden soll. Wenn eine Jobnummer angegeben wird, wird dieser ein Prozentzeichen vorangestellt, damit sie von Prozess-IDs unterschieden werden kann. Es können auch Prozess-IDs oder Jobnummern mehrerer Prozesse angegeben werden. Sie erhalten dann alle dasselbe Signal.
72
2 Linux/UNIX
Um den Job mit der Nummer 1 zu beenden, ist folgendes Kommando einzugeben: joe@debian:~$ kill %1 Um den Prozess mit der PID 2467 zu beenden, ist folgendes einzugeben: joe@debian:~$ kill 2467 Und um denselben Prozess brutal zu beenden, ohne ihm Chancen zum Aufräumen zu gewähren, ist folgendes Kommando einzugeben: joe@debian:~$ kill -9 2467 Alternativ könnte man auch schreiben: joe@debian:~$ kill -SIGKILL 2467 Vorteilhaft bei der Benutzung von Prozess-IDs gegenüber der Benutzung von Jobnummern ist der Umstand, dass mit ihnen auch Signale an Prozesse geschickt werden können, die nicht von der Bash kontrolliert werden, mit der gerade gearbeitet wird. Natürlich ist dabei zu beachten, dass nur solche Prozesse gekillt werden dürfen, die einem auch gehören, in der Regel solche, die man selbst gestartet hat. Dem Systemverwalter ist es mit kill -9 möglich, fast jeden Prozess auf dem System sofort zu beenden. Welche Prozesse auf dem System laufen und wie die zugehörigen Prozess-IDs lauten, lässt sich mit dem Programm ps herausfinden: joe@debian:~$ ps -ef | less Die Option -e (every) sorgt dafür, dass alle Prozesse auf dem System angezeigt werden, nicht nur die eigenen. Die Option -f (full oder forest) gestaltet die Ausgabe ausführlicher, sodass sie gerade eine Bildschirmbreite (80 Zeichen) füllt. Weitere Optionen sowie die Bedeutung der ausgegebenen Spalten erklären das Manual sowie Abschnitt 2.6 Prozesse auf Seite 41. 2.7.4 Aneinanderreihung von Kommandos Der Operator ; (Semikolon) Neben der Verkettung von Kommandos mit dem Pipe-Operator ist es möglich, auch mehrere unverkettete Kommandos in eine Zeile zu schreiben. So ist es sinnvoll, Kommandos, die immer nacheinander ausgeführt werden sollen, in eine Zeile zu schreiben. Sie stehen dann über die Kommandogeschichte sofort alle zusammen wieder zur Verfügung und brauchen nicht einzeln herausgefischt zu werden. In einer Zeile stehende Kommandos werden mit dem Zeichen ; (Semikolon) voneinander getrennt. Sie werden dann sequentiell (nacheinander) abgearbeitet. Das heißt, erst wenn das erste Kommando beendet ist, wird das zweite ausgeführt usw. Dies lässt sich an folgendem Beispiel erklären. Weiter oben sollte mit dem Kommando:
2.7 Kommandointerpreter (Bash)
73
joe@debian:~$ ls -l diplom | cut -c 34-41 > > groesse der Datei groesse die Größe der Datei diplom nach jedem Aufruf des Kommandos angehängt werden. Nun wäre es hübsch, nach jedem Aufruf einen Kommentar in die betreffende Datei zu schreiben. Nach dem Aufruf des obigen Kommandos kann der Editor vi benutzt werden, um den Kommentar einzutragen: joe@debian:~$ ls -l diplom | cut -c 34-41 > > groesse; vi groesse Diese Formulierung sichert, dass der Editor erst dann gestartet wird, wenn die Ausgabe der vorhergehenden Kommandos an die Datei groesse angehängt wurde. Würde man stattdessen schreiben: joe@debian:~$ ls -l diplom | cut -c 34-41 > > groesse & vi groesse dann würde der Editor fast zeitgleich mit dem vorhergehenden Kommando aufgerufen, und das Ergebnis der Größenbestimmung würde voraussichtlich noch gar nicht in der Datei zu finden sein. Bedingte Ausführung mit den Operatoren && und || Gelegentlich soll ein Kommando nur dann ausgeführt werden, wenn ein vorhergehendes Kommando ein bestimmtes Ergebnis geliefert hat. Dazu muss man wissen, dass jedes Programm einen Rückgabewert (E: return value, exit status, F: code retour) an die Shell oder einen anderen aufrufenden Prozess liefert, wenn es beendet wird. Dieser Rückgabewert ist eine ganze Zahl zwischen 0 und 255 einschließlich. Per Konvention berichtet ein Programm mit dem Rückgabewert 0, dass alles gut gegangen ist und keine Fehler aufgetreten sind. Mit einem von 0 abweichenden Wert wird angezeigt, dass Fehler aufgetreten sind. Weil es 255 verschiedene Rückgabewerte gibt, die einen Fehler anzeigen, besteht die Möglichkeit, über die Rückgabewerte auf unterschiedliche Fehler hinzuweisen. So könnte der Rückgabewert 1 Datei nicht gefunden und der Rückgabewert 2 unbekannte Option angegeben bedeuten. Hierfür gibt es keine einheitliche Regelung; die Bedeutung eines von 0 abweichenden Rückgabewertes ist vom jeweiligen Programm abhängig. Handelt es sich um Kommandos, findet sich die Bedeutung der Rückgabewerte im Manual. Der Operator && bewirkt, dass das auf den Operator folgende Kommando nur dann ausgeführt wird, wenn das davor stehende Kommando den Rückgabewert 0 geliefert hat. Beispielsweise führt das folgende Kommando dazu, dass der Editor vi nur dann aufgerufen wird, wenn das Programm ls am Anfang der Zeile die Datei diplom anzeigen konnte: joe@debian:~$ ls -l diplom >> protokoll && vi protokoll Im Gegensatz dazu bewirkt der Operator ||, dass das auf ihn folgende Kommando nur ausgeführt wird, wenn das voranstehende Kommando einen von 0 abweichenden Rückgabewert geliefert hat. Im folgenden Beispiel wird der Editor nur dann aufgerufen, wenn das Kommando ls die Datei diplom nicht anzeigen konnte:
74
2 Linux/UNIX
joe@debian:~$ ls -l diplom >> protokoll || vi protokoll Auch diese Operatoren führen dazu, dass die angegebenen Kommandos nacheinander abgearbeitet werden. Logischerweise muss der Rückgabewert der Kommandos vor den Operatoren feststehen, bevor entschieden werden kann, ob die folgenden Kommandos ausgeführt werden sollen oder nicht. Bei einer Kette von Kommandos vor dem Operator wird der Rückgabewert des zuletzt ausgeführten Kommandos ausgewertet. Das folgende Kommando würde den Editor immer aufrufen, auch wenn die Datei diplom gar nicht angezeigt werden konnte, weil zwar ls einen von 0 abweichenden Rückgabewert liefert, aber das Programm cut auch dann den Wert 0 zurückgibt, wenn es eine leere Datei bearbeitet: joe@debian:~$ ls -l diplom | cut -c 34-41 > > groesse && vi groesse Gruppieren von Kommandos Gelegentlich möchte man eine Gruppe von Kommandos oder Programmen zusammenfassen, um sie gemeinsam aus der gleichen Datei lesen zu lassen oder zusammen in den Hintergrund zu stellen. Hierzu dienen die Operatoren {} und (). Beispielsweise bewirkt das Kommando: joe@debian:~$ ( ls; pwd; whoami ) & dass die Kommandos ls, pwd und whoami nacheinander im Hintergrund ausgeführt werden. Das ist wichtig, wenn die Ausgabe dieser Kommandos in eine Datei umgeleitet werden sollen. Würde man das Kommando: joe@debian:~$ ls > > datei & pwd > > datei & whoami > > datei & eingeben, dann würden die drei Programme unabhängig voneinander nahezu zeitgleich gestartet und asynchron ausgeführt werden. Es wäre eine Frage des Zufalls bzw. des schnellsten Programms, welches Programm zuerst in die Ausgabedatei datei schreiben würde, und man könnte den Aufbau dieser Datei schlecht voraussagen. Auf der anderen Seite bewirkt das folgende Kommando, dass die drei Programme hintereinander ausgeführt werden und deswegen ihre Ausgaben auch hintereinander in die Ausgabedatei schreiben. Hier steht genau fest, dass zunächst die Ausgabe von ls, dann die von pwd und zum Schluss die von whoami in der Ausgabedatei landet. Das Ergebnis kann hinterher ausgewertet werden. Der Operator >> wird nicht benötigt, um die Ausgaben aller Prozesse zu speichern, weil die Bash die in Klammern zusammengefassten Kommandos als einen einzigen Prozess betrachtet: joe@debian:~$ ( ls; pwd; whoami ) > datei & Der ()-Operator bewirkt, dass eine weitere Shell gestartet wird, in der die angegebenen Kommandos ausgeführt werden. Diese weitere Shell lässt sich genauso wie jeder andere Prozess durch die Login-Shell verwalten, beispielsweise wieder in den
2.7 Kommandointerpreter (Bash)
75
Vordergrund schalten. Letztlich ist es die Ausgabe dieser Shell (in der die angegebenen Kommandos ausgeführt werden), die mit dem Operator > in die Datei datei umgeleitet wird. Das Verfahren hat den Nachteil, dass alle Variablen, die durch die geklammerten Kommandos geändert werden, verloren gehen, weil sie in der neu gestarteten Shell geändert und gespeichert werden und diese beendet wird, sobald alle darin auszuführenden Kommandos abgearbeitet sind. Wenn anstatt der runden Klammern geschweifte Klammern benutzt werden, führt dies dazu, dass die geklammerten Kommandos von derselben Shell ausgeführt werden: joe@debian:~$ { ls; pwd; whoami; a=17; } > datei Der Wert der Variablen a bleibt nach Abarbeitung des Kommandos verfügbar. Bei Verwendung geschweifter Klammern müssen zwischen Klammern und Kommandos Leerzeichen stehen, und jedes Kommando muss mit einem Semikolon abgeschlossen sein. Der &-Operator führt auch bei dieser Konstruktion dazu, dass die angegebenen Kommandos im Hintergrund ausgeführt werden, wodurch Variable verloren gehen. 2.7.5 Variable Positionsvariable Die Bash kennt wie andere Shells Variable, manchmal Parameter genannt, deren Werte Zeichenketten oder Arrays (Felder, Feldvariable) von Zeichenketten sind. Einige Kommandos fassen die Zeichenketten je nach Zusammenhang auch als numerischen oder logischen Wert auf, ohne Zutun des Benutzers. Es gibt zwei Arten von Variablen: • •
Positionsvariable (Aufrufparameter, Positionsparameter, Parametervariable), benannte Variable.
Die benannten Variablen werden weiter unterteilt; wir befassen uns damit noch ausführlich. Positionsvariable werden von der Bash automatisch verwaltet und enthalten Informationen aus der Kommandozeile. In der Regel werden sie vom Benutzer und seinen Programmen nur gelesen. Ihre Bezeichnungen und Bedeutungen sind: • • • •
•
$0 enthält das erste Glied der Kommandozeile, das heißt das Kommando selbst ohne Optionen oder Argumente. $1 enthält das zweite Glied der Kommandozeile, meist eine Option oder ein Argument. So geht es weiter bis $9. $* (Dollar Stern) enthält die ganze Kommandozeile ohne $0 als eine einzige Zeichenkette. $@ (Dollar Klammeraffe) enthält die ganze Kommandozeile ohne $0, jedes Glied in einer eigenen Zeichenkette. Dieser Unterschied zu $* spielt gelegentlich eine Rolle. $# (Dollar Doppelkreuz) enthält die Anzahl der Glieder der Kommandozeile ohne $0.
76
• • •
2 Linux/UNIX
$? (Dollar Fragezeichen) enthält den Rückgabewert (Returnwert, Exit Code/Status) des zuletzt im Vordergrund ausgeführten Kommandos. $! (Dollar Ausrufezeichen) enthält die Prozess-ID des jüngsten HintergrundProzesses. $$ (Dollar Dollar) enthält die Prozess-ID der Shell.
Manchmal werden nur die mit Ziffern bezeichneten Parameter als Positionsparameter angesehen, die nachfolgenden als Spezialparameter. Es gibt noch einige Variable von der Sorte, siehe das Manual zur Bash, Suchwort Positional Parameters. Eine Kommandozeile kann natürlich mehr als zehn Glieder umfassen. Die Glieder jenseits von $9 sind auch zugänglich, aber auf andere Weise. Mit dem Kommando: joe@debian:~$ shift n verschiebt man alle Positionsvariablen von $1 angefangen um n Positionen nach links (zu niedrigeren Zahlen). Die vorher am linken Ende stehenden Positionsvariablen fallen dabei weg. Die im so genannten Sumpf gelegenen Positionsvariablen mit Nummern höher 9 rutschen entsprechend in niedrigere Positionen und werden auf die beschriebene Weise nutzbar. Der Vorgabewert von n ist 1. Um die Wirkungsweise besser zu verstehen, schreiben wir uns ein kleines Shellskript namens posi: echo $* echo $0 echo $1 $2 $3 echo $9 $10 $11 shift echo $1 $2 $9 setzen seine Zugriffsrechte: joe@debian:~$ chmod 700 posi und rufen es mit zehn Argumenten auf: joe@debian:~$ ./posi eins zwei drei vier fuenf sechs sieben acht neun zehn In der esten Zeile der Ausgabe finden wir erwartungsgemäß die gesamte Eingabezeile ohne das Kommando ./posi wieder, in der zweiten Zeile das Kommando. Die dritte Zeile nennt die ersten drei Argumente. In der vierten taucht ein Problem auf, da das zehnte Argument und alle folgenden nicht auf diese Weise angesprochen werden können. In der fünften Ausgabezeile sehen wir, dass das erste Argument durch das zweite ersetzt worden ist und das neunte durch das vorher unzugängliche zehnte. Den Positionsvariablen lassen sich nicht wie benannten Variablen neue Werte zuweisen. Allerdings ist es möglich, sie über das eingebaute Kommando set neu zu setzen. Die Syntax zu diesem Kommando lautet: set Argument1 [Argument2 ...]
2.7 Kommandointerpreter (Bash)
77
Danach ist das mit Argument1 angegebene Argument in der Variablen 1 gespeichert, das mit Argument2 angegebene in der Variablen 2 usw. Wird set ohne Optionen oder Argumente aufgerufen, gibt es die Namen aller definierten Shellvariablen mitsamt ihren Werten aus. Zusammen mit dem Kommando set können auch die Ersetzungsmechanismen benutzt werden. So weist das Kommando: joe@debian:~$ set * die Namen aller Dateien im Arbeitsverzeichnis (die durch die Dateinamenerweiterung für den Stern eingesetzt werden) den Positionsvariablen zu. Das Kommando: joe@debian:~$ set ‘ls -l diplom‘ führt dazu, dass die Ausgabe des Kommandos ls -l diplom in den Positionsvariablen gespeichert wird. In der Variablen 1 sind dann die Zugriffsrechte der Datei gespeichert, in der Variablen 2 die Anzahl der Verzeichniseinträge (link counter), in der Variablen 3 der Besitzer usw. Wenn nicht jedes durch Leerzeichen getrennte Wort einer einzelnen Variablen zugeordnet werden soll, sondern die Zerlegung nach einem anderen Muster erfolgen soll, ist die Variable IFS vorübergehend entsprechend zu ändern. Will man eine temporäre Datei mit einem eindeutigen Namen erzeugen, lässt sich dazu die eindeutige Prozess-ID der jeweiligen Shell heranziehen: joe@debian:~$ cp datei datei.$$ woraus folgt, dass man mit Dollarzeichen in Dateinamen vorsichtig umgehen soll. Andernfalls ereignen sich möglicherweise ungewollte Dinge. Die Hauptanwendung der Positionsvariablen liegt in Shellskripten, siehe Abschnitt 2.7.7 Shellskripte auf Seite 93. Benannte Variable Benannte Variable der Shell tragen einen Namen (Bezeichner, Identifier) und werden über diesen angesprochen. Einige erzeugt die Shell von sich aus, andere kann oder muss der Benutzer anlegen. Anwendungsprogramme richten auch gern solche Variable ein. Die meisten lassen sich sowohl lesen wie ändern. Die Namen bestehen aus einem großen oder kleinen Buchstaben, gefolgt von Buchstaben, Ziffern oder dem Unterstrich (E: underscore, F: tiret bas, sousligné). Die Variablen haben einen Gültigkeitsbereich (E, F: scope); Programmierer kennen den Begriff: • •
Lokal gültige Variable gelten nur in der Shell, in der sie erzeugt worden sind (Normalfall). Global gültige Variable gelten in der Shell, in der sie erzeugt worden sind, und in deren Abkömmlingen. Man sagt, sie werden exportiert.
Ein Export nach oben, an die aufrufende Shell, ist nicht möglich. Die Ursache ist das Verfahren, mit dem Prozesse erzeugt werden. Benannte Variable werden erzeugt und erhalten ihren Wert durch eine Zuweisung der Form:
78
2 Linux/UNIX
joe@debian:~$ NAME=wert Die Großschreibung der Namen ist nicht zwingend, aber verbreitet. Um das Gleichheitszeichen7 herum dürfen keine Leerzeichen stehen. Ist der Wert – eine Zeichenkette – aus mehreren getrennten Zeichengruppen zusammengesetzt, muss er von einfachen oder doppelten Anführungszeichen eingerahmt werden: joe@debian:~$ NAME="Debian GNU/Linux" Der Wert einer bereits existierenden Variablen wird ebenfalls durch eine Zuweisung geändert. Auch die leere Zeichenkette darf zugewiesen werden. Erst wenn auf der rechten Seite der Zuweisung nichts steht: joe@debian:~$ NAME= gilt die Variable als undefiniert und wird gelöscht. Dasselbe bewirkt das eingebaute Shellkommando unset: joe@debian:~$ unset NAME Bestimmte vordefinierte Shellvariable verlieren ihre besonderen Eigenschaften beim ersten unset, auch wenn sie hernach wieder gesetzt werden, beispielsweise die Variablen GROUPS, HISTCMD oder RANDOM. Einer Variablen darf der Wert einer anderen, bereits vorhandenen (definierten) Variablen zugewiesen werden: joe@debian:~$ BENUTZER=$USER Soll an den Wert unmittelbar eine Zeichenkette angehängt werden, ist der Variablenname zu klammern: joe@debian:~$ BENUTZER=${USER}.debian andernfalls würde das Anhängsel als Teil des Namens verstanden werden. Man darf sogar einer Variablen ihren geänderten oder ergänzten alten Wert als neuen Wert zuweisen: joe@debian:~$ PATH=~/bin:"$PATH" womit dem vom System eingestellten Kommandopfad das Verzeichnis bin im Home-Verzeichnis des Benutzers vorangestellt wird. Die Shell unterscheidet zwischen dem alten und dem neuen Wert. Mit dem eingebauten Shellkommando set ohne Argumente erfährt man alle aktuell gültigen Shellvariablen in alphabetischer Folge und ihre Werte: joe@debian:~$ set | less üblicherweise der Anzahl nach mehrere Dutzend, daher zweckmäßig durch less zu filtern. Wir sehen uns bald die wichtigsten an. Will man eine Variable nur für ein Kommando oder Programm erzeugen oder ändern, so muss man dafür sorgen, dass Zuweisung und Kommando gemeinsam als Gruppe (list) in einer Subshell ausgeführt werden: 7
Eine Zuweisung ist keine Gleichung. Trotzdem reden wir von Gleichheitszeichen, um nicht unnötig einen neuen Begriff einzuführen. Beim Programmieren wird zwischen Zuweisung (E: assignment) und Gleichung (E: equation) streng unterschieden.
2.7 Kommandointerpreter (Bash)
79
joe@debian:~$ ( NAME=wert; /bin/echo $NAME ) Hier gibt das Kommando /bin/echo die Zeichenkette wert aus, unabhängig davon, welchen Wert die Variable NAME vorher hatte. Die Klammern verhindern, dass die Variable NAME in der Sitzungsshell gesetzt und an den Kindprozess /bin/echo vererbt wird. Hinterher hat die Variable NAME wieder ihren alten Wert, da der neue Wert nicht von der Subshell rück- oder aufwärts vererbt wird. Einige wenige Variable können vom Benutzer nur gelesen, aber nicht verändert werden. Man erfährt sie mittels: joe@debian:~$ readonly Beispielsweise ist die Variable EUID, welche die effektive Benutzer-ID enthält, nicht vom Benutzer durch eine Zuweisung veränderbar: joe@debian:~$ EUID=4711 erzeugt eine Fehlermeldung. Ein Benutzer kann auch selbst unveränderbare Variable definieren: joe@debian:~$ readonly ABC=abc Der Versuch, die Variable ABC zu verändern oder zu löschen, resultiert in der gleichen Fehlermeldung. Der Benutzer wird sie los, indem er die Shell, in der sie definiert wurde, beendet. Neu angelegte Variable sind per Vorgabe nur lokal gültig; sie gelten nur in der Shell, in der sie angelegt werden. Sollen sie an Kindprozesse weiter vererbt (exportiert) werden, ist ein zweiter Schritt erforderlich. Der Export von Variablen ist ein Weg, auf dem Shells oder andere Prozesse ihren Kindprozessen etwas mitteilen, beispielsweise die Namen bestimmter Dateien oder Verzeichnisse. Die aktuellen zum Export vorgesehenen Variablen – die Umgebung der Shell – erfährt man mittels: joe@debian:~$ export Die Ausgabe besteht aus Zeilen wie: declare -x TERM="xterm" Das eingebaute Kommando declare mit der Option -x erzeugt neue Variable und sieht sie zum Export vor. Seine Möglichkeiten gehen weiter, siehe man bash, Suchwort declare. Die Umgebung (E: environment, F: environnement) eines Prozesses besteht aus der ursprünglichen Umgebung der aufrufenden Shell, gegebenenfalls mit geänderten Werten, abzüglich der mittels unset oder anders gelöschten Variablen und zuzüglich der mittels export oder declare -x hinzugefügten Variablen. Mit dem export- Kommando erzeugt man zu exportierende Variable, entweder zusammen mit der Zuweisung oder in getrenntem Aufruf: joe@debian:~$ export NAME=wert joe@debian:~$ NAME=wert; export NAME
80
2 Linux/UNIX
Den Export einer Variablen nimmt man mit einer Option zurück: joe@debian:~$ export -n NAME Dieselbe Wirkung zeigt: joe@debian:~$ declare +x NAME Das Pluszeichen vor der Option kehrt ihre übliche Wirkung um; das gilt nicht allgemein für jede Option und jedes Kommando. Bei der Zuweisung eines neuen Wertes an eine bereits vorhandene exportierte Variable bleibt diese global gültig; ein erneuter Export erübrigt sich. Prozesse, die bereits mit dem alten Wert der exportierten Variablen laufen, erfahren nichts von der Änderung. Nur nach der Änderung gestartete Prozesse laufen mit dem neuen Wert. Zur Anzeige der zum Export vorgesehenen Variablen dient auch das Kommando printenv (print environment). Eine Reihe von Variablen wird von der Login- oder Sitzungsshell erzeugt und an alle Prozesse der Sitzung vererbt oder exportiert. Diese Variablen bilden die Umgebung der Sitzung und werden in Shellskripten wie /etc/profile und /etc/bash.bashrc systemweit sowie in benutzereigenen Skripten wie $HOME/.profile, $HOME/bash_profile und $HOME/.bashrc definiert. Auf eine benannte Variable wird in der Kommandozeile oder in einem Shellskript zugegriffen, indem man ihrem Namen ein Dollarzeichen voranstellt: joe@debian:~$ echo $TERM Obige Eingabe veranlasst die Anzeige des Wertes der Variablen TERM, beispielsweise xterm oder hpterm. Achtung: echo ist sowohl ein eingebautes Shellkommando – siehe man bash – als auch ein externes Kommando /bin/echo – siehe man echo. Beide unterscheiden sich leicht. Auch in letzterem Fall wird der Ausdruck $TERM von der Shell durch seinen Wert ersetzt (expandiert), das Kommando /bin/echo erfährt nur den Wert der Variablen. Sehen wir uns nun die wichtigsten benannten Shellvariablen an, in alphabetischer Folge, wie sie das Kommando set ausgibt, zuzüglich einiger Variablen, deren Wert man sich mit dem echo-Kommando ansehen kann. Auf Ihrem Rechner wird die Aufzählung in Einzelheiten anders aussehen; die hier teilweise verkürzt wiedergegebenen Werte oder Wertelisten weichen mit Sicherheit ab: • • • • • •
BASH=/bin/bash nicht exportiert, absoluter Pfad der aktuellen Shell, BASH_VERSINFO=([0]="2"[1]="05b"...) nicht exportiert, Version der aktuellen Shell als Array dargestellt, BASH_VERSION=’2.05b.0(1)-release’ nicht exportiert, Version der aktuellen Shell als durchgehende Zeichenkette dargestellt, CDPATH=.:~:/usr:/usr/local nicht exportiert, Suchpfad für das cdKommando, COLORTERM=gnome-terminal exportiert, aktuelles Terminalprogramm, siehe Abschnitt 3.12 Terminal-Emulatoren auf Seite 225, COLUMNS=91 nicht exportiert, zum Terminalprogramm gehörende Spaltenzahl (Fensterbreite in Anzahl der Zeichen angegeben), siehe auch LINES,
2.7 Kommandointerpreter (Bash)
•
•
•
• • •
•
• •
• •
• •
• •
• •
81
CVS_RSH=ssh exportiert, in der benutzereigenen Datei $HOME/.bashrc festgelegtes, von der Anwendung CVS (Concurrent Versions System) zu verwendendes Programm für den Zugriff auf das CVS-Repository über Netz, hier die Secure Shell, DIRSTACK=() nicht exportiert, ein Array, hier leer, das eine Liste von Verzeichnissen speichert; lässt sich mittels der eingebauten Kommandos pushd und popd ändern und mit dirs anzeigen, gelegentlich ein bequemer Weg zum Wechseln zwischen Verzeichnissen, DISPLAY=:0.0 exportiert, Nummer des X11-Displays, mit dem etwaige X11Clients Verbindung aufnehmen sollen, siehe Abschnitt 9.4 X Window System ab Seite 407, EDITOR=/usr/bin/vi nicht exportiert, nennt den Editor, mit dem als Vorgabe Kommandozeilen editiert werden sollen, auch sonst verwendet, EUID=1001 nicht exportiert, effektive Benutzernummer, siehe auch UID sowie Abschnitt 12 Benutzer und Gruppen ab Seite 519, FCEDIT=/usr/bin/vi nicht exportiert, nennt den Editor, mit dem als Vorgabe Kommandozeilen editiert werden sollen, die aus der Kommandogeschichte zurückgeholt worden sind (eingebautes Kommando fc), FIGNORE=".o: :.bak" nicht exportiert, Liste von Dateikennungen, die bei der Dateinamenerweiterung durch readline-Funktionen zum Übergehen der betreffenden Datei führt, FUNCNAME=... nicht exportiert, existiert nur, wenn gerade eine Shellfunktion ausgeführt wird, siehe Abschnitt 2.7.7 Shellskripte auf Seite 93, GDM_XSERVER_LOCATION=local exportiert, Wirkungsstätte des XServers, aufgerufen vom GNOME Display Manager, hier der lokale Arbeitsplatzrechner, siehe Abschnitt 3.4 Display-Manager auf Seite 195, GROUPS=() nicht exportiert, Array der Gruppen als Gruppen-ID, zu denen der Benutzer gehört, siehe Abschnitt 12 Benutzer und Gruppen ab Seite 519, GTK_RC_FILES=/etc/gtk/gtkrc:/home/joe/.gtkrc-1.2 exportiert, absolute Pfade der systemweiten und der benutzereigenen Konfigurationsdatei des GIMP Toolkits, einer viel verwendeten Grafik-Bibliothek, HISTCMD=561 nicht exportiert, laufende Nummer (Index) des gerade ausgeführten Kommandos in der Kommandogeschichte, HISTFILE=/home/joe/.bash_history nicht exportiert, Datei mit der Kommandogeschichte (history), siehe Abschnitt 2.7.2 Kommandogeschichte auf Seite 62, HISTFILESIZE=500 nicht exportiert, maximale Anzahl der Einträge in vorstehender Datei, HISTSIZE=500 nicht exportiert, Anzahl der Kommandozeilen, die beim Beenden einer interaktiven Shell in die Datei mit der Kommandogeschichte (HISTFILE) kopiert werden, höchstens gleich HISTFILESIZE, HOME=/home/joe exportiert, absoluter Pfad des Home-Verzeichnisses des Benutzers, HOSTNAME=pcjoe nicht exportiert, Name des Rechners, wie vom Kommando hostname angezeigt,
82
•
•
•
•
•
• • • • • •
• • • • • •
•
2 Linux/UNIX
HOSTTYPE=i386 nicht exportiert, Architektur des Rechners, hier Intel 86386 und Nachfolger (tatsächlich ein AMD Athlon, aber der Prozessor fällt unter diese Architektur), IFS=$’ \t\n’ nicht exportiert, Internal Field Separator, Trennzeichen zwischen den Teilen einer Kommandozeile, hier Dollarzeichen, Leerzeichen, Tabulator und Zeilenwechsel (newline), LANG=de_DE@EURO exportiert, Variable aus dem Locale, siehe Abschnitt 9.5 Locale auf Seite 418, wo auch weitere unter Umständen gesetzte Umgebungsvariable aus dem Locale erläutert sind (LC_ALL usw.), LANGUAGE=de_DE:de:en_GB:en exportiert, Variable aus dem Locale, legt fest, in welcher sprachlichen Folge nach Manualseiten und anderen Texten gesucht werden soll, hier deutsches Deutsch, Deutsch allgemein, britisches Englisch, Englisch allgemein, LINENO=71 nicht exportiert, Zeilennummer des gerade ausgeführten Kommandos. Der Wert entspricht bei interaktiven Shells der Nummer der jeweiligen Kommandozeile, in Shellskripten der Zeilennummer des gerade ausgeführten Kommandos im Skript. LINES=28 nicht exportiert, zum Terminalprogramm gehörende Zeilenzahl (Fensterhöhe), siehe auch COLUMNS, LOGNAME=joe exportiert, Benutzername wie beim Einloggen verwendet, ändert sich nicht während einer Sitzung, siehe auch USERNAME, LS_COLORS=’no=00:...:.wav=01;35:’ exportiert, vom Werkzeug ls zu verwendende Farben zur Kennzeichnung verschiedener Dateitypen, MACHTYPE=i386-pc-linuc-gnu nicht exportiert, Maschinentyp, das heißt Architektur und Betriebssystem, MAILCHECK=600 nicht exportiert, Periode in Sekunden zum Abfragen des Posteingangs nach etwaigen neuen Mails, MAILPATH="/var/spool/mail/debian:/var/spool/mail/joe" nicht exportiert, Liste von Dateien, die eingehende Mail enthalten, siehe Abschnitt 16.11 Electronic Mail auf Seite 797, OLDPWD=/home/joe/TeX/d1/Haupt exportiert, vorheriges Arbeitsverzeichnis, siehe auch PWD, OPTARG=... nicht exportiert, der Wert der letzten von dem eingebauten Kommando getopts verarbeiteten Option. OPTERR=1 nicht exportiert, mit dem Wert 1 zeigt die Bash etwaige Fehlermeldungen des eingebauten Kommandos getopts an. OPTIND=1 nicht exportiert, gehört ebenfalls zum Kommando getopts, OSTYPE=linux-gnu nicht exportiert, das Betriebssystem, unter dem die Shell läuft, PATH=/usr/local/bin:/usr/bin:/bin:... exportiert, Suchpfade für externe Kommandos, die Reihenfolge der Verzeichnisse spielt eine Rolle, siehe Abschnitt 2.7.2 Externe und interne Kommandos auf Seite 59, PIPESTATUS=([0]="0") nicht exportiert, ein Array, das die Rückgabewerte der Prozesse der zuletzt im Vordergrund ausgeführten Pipe enthält,
2.7 Kommandointerpreter (Bash)
•
•
•
•
• •
• • • •
• • • • • • • • •
83
PPID=1616 nicht exportiert, Nummer des Elternprozesses (parent process identifier) der Shell, hier die Nummer eines GNOME-Terminal-Prozesses als Elternprozess der Bash, PROMPT_COMMAND=’echo -ne "\033]0;${USER}@${HOSTNAME}; ${PWD}\007"’ nicht exportiert, falls gesetzt, wird der Wert als Kommando vor jedem primären Prompt ausgeführt, PS1=’${debian_chroot:+(debian_chroot)}\u@\h;\w\$ ’ nicht exportiert, erster (primärer) Prompt, siehe Abschnitt 2.2 Kommandozeile auf Seite 30, PS2=’>= ’ nicht exportiert, zweiter Prompt, der angezeigt wird, wenn ein Kommando noch nicht vollständig eingegeben, aber schon die-Taste gedrückt wurde (also noch Eingaben fehlen), PS3= nicht exportiert, dritter Prompt, falls gesetzt, vom select-Kommando in Shellskripten (Selektion, Auswahl) verwendet, PS4=”+ ’ nicht exportiert, vierter Prompt, der während der Ablaufverfolgung (tracing) eines Shellskripts benutzt werden kann, siehe Abschnitt 2.7.7 Shellskripte auf Seite 93, PWD=/home/joe exportiert, augenblickliches Arbeitsverzeichnis, siehe auch OLDPWD sowie Abschnitt 2.8.9 Arbeiten mit Verzeichnissen auf Seite 139, RANDOM=23541 nicht exportiert, eine bei jeder Abfrage neu erzeugte zufällige Ganzzahl zwischen 0 und 32767 zur beliebigen Verwendung, SECONDS=30648 nicht exportiert, Anzahl der Sekunden seit dem Start der Shell, SESSION_MANAGER=local/pcjoe:/tmp/.ICE-unix/1465 exportiert, vom Display-Manager gesetzte Variable mit dem Namen einer leeren Datei, der die Prozess-ID des X-Session-Managers darstellt, siehe Abschnitt 3.5 Session-Manager auf Seite 200, SHELL=/bin/bash exportiert, absoluter Pfad der Login-Shell, siehe auch BASH, SHELLOPTS=braceexpand:emacs:hashall:... nicht exportiert, Optionen der aktuellen Shell, SHLVL=1 exportiert, Zähler, der mit jedem Aufruf einer Subshell um 1 erhöht wird (Schachtelungstiefe), SSH_AGENT_PID=1507 exportiert, Prozess-ID des Secure-Shell-Agenten, der die privaten Schlüssel verwaltet, SSH_AUTH_SOCK=/tmp/ssh-MjNg1465/agent.1465 exportiert, eine Variable der Secure Shell, TERM=xterm exportiert, Typ des Terminals, wichtig für die Wahl der richtigen Terminalkommandos, UID=1001 nicht exportiert, Benutzernummer beim Start der Shell, siehe auch EUID und USER sowie Abschnitt 12 Benutzer und Gruppen ab Seite 519, USER=joe exportiert, Benutzername, siehe auch UID, USERNAME=joe exportiert, dasselbe wie LOGNAME
84
•
•
2 Linux/UNIX
XAUTHORITY=/home/joe/.Xauthority exportiert, von X11 gesetzte Variable, Name der Datei mit Schlüsseln (MIT Magic Cookies) zugelassener XClients, siehe man xauth, _=mutt nicht exportiert, Name des jüngstvergangenen Kommandos.
Einige Werte – zum Beispiel der Benutzername – werden unter mehreren Variablen gespeichert. Das rührt daher, dass verschiedene Programme verschiedene Variablen abfragen. Es gibt auch Werte wie die Anschrift für Antworten auf Mails (REPLYTO=...), die sich entweder in der Umgebung oder in der Konfigurationsdatei ($HOME/.muttrc) einer Anwendung speichern lassen. Macht man beides, erhebt sich die Frage nach dem Vorrang. 2.7.6 Expansion (Erweiterung) und Substitution (Ersetzung) Dateinamenerweiterung (Globbing) Die Begriffe Expansion (Erweiterung) und Substitution (Ersetzung oder Auflösung) bezeichnen ähnliche Vorgänge. In beiden Fällen wird ein Ausdruck durch einen anderen ersetzt. Ein einfacher Fall wurde bereits bei der Benutzung von Variablen vorgestellt: joe@debian:~$ a=$b Die Anweisung bewirkt, dass der Ausdruck $b durch die Zeichenkette, die in der Variablen b gespeichert ist, ersetzt und der Variablen a zugewiesen wird. Die Shell beherrscht verschiedene Expansions- und Substitutionsmechanismen, mit denen sich – in begrenztem Umfang – sogar arithmetische Berechnungen durchführen lassen oder die Ausgabe eines Programms einer Variablen zugewiesen werden kann. Sobald die Shell auf einen Datei- oder Verzeichnisnamen trifft, der die Metazeichen (Jokerzeichen, wildcard) * (Stern, E: asterisk, F: astérisque), ? (Fragezeichen, E: question mark, F: point d’interrogation) oder [] (eckige Klammern, E: brackets, F: crochets) enthält, versucht sie, die Zeichenkette durch alle Namen vorhandener Dateien oder Verzeichnisse zu ersetzen, auf die Folgendes zutrifft: • • • •
Jedes Zeichen, das keine besondere Bedeutung hat, muss bei den in Frage kommenden Dateinamen an genau der gleichen Stelle stehen wie in der Zeichenkette. Das Fragezeichen bezeichnet genau ein beliebiges Zeichen. Der Stern bezeichnet eine beliebige Anzahl beliebiger Zeichen (auch 0 Zeichen). In eckigen Klammern angegebene Zeichen bezeichnen genau ein Zeichen aus der Menge der in den eckigen Klammern eingeschlossenen Zeichen. Mit einem Bindestrich wird ein Bereich von Zeichen angegeben. Wenn einer Zeichenkette in eckigen Klammern ein Ausrufungszeichen oder das ^-Zeichen vorangestellt ist, sind alle nicht angegebenen Zeichen gemeint (Komplement).
Ein Schrägstrich – das Trennzeichen zwischen Verzeichnissen in Pfaden – wird nicht durch ein Metazeichen erfasst, sondern muss ausdrücklich hingeschrieben werden. Braucht man eines der Metazeichen buchstäblich, ist es durch einen Gegenschrägstrich zu quoten, siehe unten. Der Mechanismus wird auch als Globbing bezeichnet
2.7 Kommandointerpreter (Bash)
85
und durch die Shelloption -f oder -o noglob unterbunden. Er hat trotz einiger Ähnlichkeit nichts mit regulären Ausdrücken zu tun, wie sie in Abschnitt 2.10.1 Reguläre Ausdrücke auf Seite 157 erläutert werden. Die Wirkung der Metazeichen lässt sich ebenfalls mit dem Kommando echo vorführen: joe@debian:~$ echo * Das Kommando gibt die Namen aller Dateien im Arbeitsverzeichnis aus. joe@debian:~$ echo /usr/share/doc/*/*.txt gibt die Namen aller Dateien in allen Unterverzeichnissen des Verzeichnisses /usr/share/doc aus, die die Kennung .txt tragen. joe@debian:~$ echo a????.txt gibt die Namen aller Dateien im Arbeitsverzeichnis aus, die mit einem a beginnen, dann aus genau vier beliebigen Zeichen bestehen und auf .txt enden. joe@debian:~$ echo [a-z][!0-9][mn]??.* gibt die Namen aller Dateien im Arbeitsverzeichnis aus, die mit einem klein geschriebenen Buchstaben beginnen, als zweites Zeichen keine Ziffer haben, deren drittes Zeichen entweder m oder n ist, die dann aus zwei weiteren beliebigen Zeichen und einem Punkt bestehen und dahinter eine beliebige Zeichenfolge aufweisen. Die Metazeichen werden von der Shell und nicht von den aufgerufenen Programmen verarbeitet. Diese erhalten von der Shell eine Liste mit Namen ohne Metazeichen und merken vom Globbing nichts. Wenn das Arbeitsverzeichnis beispielsweise die Dateien bild1.png, bild2.png und bild3.png enthält und folgendes Kommando eingegeben wird: joe@debian:~$ ls -l *png dann sieht die Shell nach, welche Dateinamen im Arbeitsverzeichnis auf die angegebene Zeichenkette passen, und setzt diese dafür ein. Das Kommando ist gleichbedeutend mit: joe@debian:~$ ls -l bild1.png bild2.png bild3.png Nur dann, wenn die Shell keinen passenden Dateinamen findet, wird er wie eingegeben an das aufgerufene Programm weitergereicht. Dateinamen, die Metazeichen enthalten, sind aber kein guter Einfall. Verarbeiten von Sonderzeichen unterdrücken (quoten) Manchmal ist es erwünscht, dass Metazeichen nicht von der Shell verarbeitet werden. Außerdem kennt die Shell weitere Metazeichen wie das Dollarzeichen. Es gibt drei Möglichkeiten, die Interpretation der Metazeichen im Einzelfall zu unterbinden (quoten, maskieren, entwerten, sperren):
86
•
•
•
2 Linux/UNIX
Wird einem Zeichen ein Gegenschrägstrich (backslash) (\) vorangestellt, so wird dieses Zeichen nicht interpretiert. Wenn man das Zeichen \ selbst meint, muss man ihm ebenfalls das Zeichen \ voranstellen. Wenn eine Zeichenkette in doppelten Anführungszeichen (double quotes) (“ “) steht, werden die in ihr enthaltenen Metazeichen nicht interpretiert. Hier gibt es Ausnahmen. So werden Dollarzeichen zwischen Anführungszeichen (die Variablenbezeichnungen wie $TERM einleiten) weiterhin interpretiert. Weitere Ausnahmen sind der Accent grave (back quote), der Gegenschrägstrich und die Gänsefüßchen (double quote). Metazeichen zwischen einfachen Anführungszeichen (Apostroph, single quote) (’ ’) werden nie interpretiert. Durch einfache Anführungszeichen lässt sich die Bedeutung aller Metazeichen der Shell abschalten, ausgenommen das einfache Anführungszeichen selbst. Dies ist erforderlichenfalls durch einen Gegenschrägstrich zu quoten.
Die Verwendung der stärksten Quotung mittels einfacher Anführungszeichen an Stellen, wo doppelte Anführungszeichen ausreichen würden, ist unschädlich. Nur dort, wo man auf die genannten Ausnahmen angewiesen ist, hat man keine Wahl. Das Kommando: joe@debian:~$ ls "*png" ruft also im Gegensatz zu obigem Beispiel das Programm ls mit dem Argument *png auf. Wenn keine Datei dieses Namens im Verzeichnis existiert, erhält man eine Fehlermeldung von ls. Auf diese Weise lassen sich Datei- oder Verzeichnisnamen als Argument eingeben, die Trennzeichen wie das Leerzeichen enthalten, was in anderen Rechnerwelten vorkommt. Einige Programme interpretieren Metazeichen ebenfalls. Dazu gehören die Programme find oder zip. Wenn für die Shell bedeutsame Zeichen in Argumenten vorkommen, die unverändert Programmen übergeben werden sollen, müssen diese immer durch Anführungszeichen oder den Gegenschrägstrich vor der Interpretation durch die Shell geschützt werden. Wenn Sie sich bei einem bestimmten Ausdruck nicht sicher sind, welche Zeichen geschützt sind und welche interpretiert werden, können Sie dies mit dem echo-Kommando überprüfen. Das Kommando: joe@debian:~$ echo "$hallo Welt"’ ’\$hallo Welt gibt – vorausgesetzt, die Variable hallo ist undefiniert – folgendes aus: Welt $hallo Welt Das erste $hallo steht zwar in doppelten Anführungszeichen, in denen die Shell dennoch versucht, den Wert der Variablen hallo einzusetzen. Weil die Variable undefiniert ist, wird für $hallo kein Wert eingesetzt, und die ausgegebene Zeichenkette beginnt mit dem Leerzeichen vor Welt. Die Leerzeichen zwischen den einfachen Anführungsstrichen werden unverändert ausgegeben (sie hätten auch in doppelten Anführungsstrichen stehen können), und die darauffolgende Zeichenkette $hallo wird ebenfalls unverändert ausgegeben, weil dem Dollarzeichen hier ein Gegenschrägstrich vorangestellt ist. Das letzte Wort Welt ist das zweite Argument
2.7 Kommandointerpreter (Bash)
87
für das echo-Kommando, weil es durch ein Leerzeichen von den vorherigen Zeichen getrennt steht. Es wird ebenfalls unverändert ausgegeben, weil es keine Metazeichen enthält. Kommandosubstitution Immer wenn ein Ausdruck in rückwärtsgerichteten, einfachen Anführungsstrichen (E: back quote, F: accent grave) (‘ ‘) steht, versteht die Shell einen solchen Ausdruck als Kommando und führt es aus. Danach ersetzt sie den Ausdruck einschließlich der Back Quotes durch das, was durch dieses Kommando auf die Standardausgabe geschrieben wird. Das Kommando: joe@debian:~$ a=‘ls‘ bewirkt, dass die Standardausgabe des Kommandos ls der Variablen a zugewiesen wird. Das Ergebnis kann mit folgendem Kommando betrachtet werden: joe@debian:~$ echo $a Nutzbringend lässt sich diese Eigenschaft einsetzen, wenn die Ausgabe eines Kommandos als Argument für ein anderes Kommando dienen soll. Beispiel: Mit der Option -S des Kommandos dpkg kann man sich anzeigen lassen, welche Datei zu welchem Debian-Paket gehört. Möchte man wissen, zu welchem Paket das Programm ls gehört, gibt man folgendes Kommando ein: joe@debian:~$ dpkg -S ls Dann werden die Namen aller zu installierten Debian-Paketen gehörenden Dateien ausgegeben, in denen die Zeichenkette ls vorkommt. Zu jeder Datei wird angegeben, aus welchem Paket sie stammt. Weil diese Zeichenkette in vielen Dateinamen vorkommt, erhält man eine lange und unübersichtliche Ausgabe. Man müsste den Dateinamen genauer angeben, um eine bessere Ausgabe zu bekommen. Das Kommando which gibt den absoluten Dateinamen eines Programms aus. Der Aufruf: joe@debian:~$ which ls führt zu der Ausgabe: /bin/ls Jetzt kann man mit dem Kommando: joe@debian:~$ dpkg -S /bin/ls genauer nach dem Paket suchen, aus dem das Programm stammt. Die Zwischeninformation des Dateinamens von ls interessiert jedoch eigentlich nicht. Und außerdem macht es keinen Spaß, Ausgaben von Programmen abzutippen, um sie anderen Programmen als Eingabe zu übergeben. Mit Hilfe der Kommandosubstitution macht man es sich einfacher: joe@debian:~$ dpkg -S ‘which ls‘
88
2 Linux/UNIX
Die Kommandosubstitution führt dazu, dass zunächst which ls abgearbeitet und dessen Ausgabe dort eingesetzt wird, wo sich der Ausdruck ‘which ls‘ befindet, bevor das Kommando dpkg aufgerufen wird. dpkg wird sofort mit der Option -S und dem Argument /bin/ls aufgerufen und liefert das gewünschte Ergebnis. Zahlenrechnen Mit der Bash ist es in begrenztem Umfang möglich, arithmetische Berechnungen durchzuführen. Solche Berechnungen werden mit einem Dollarzeichen eingeleitet und in eckige Klammern gesetzt. Beispielsweise gibt das Kommando: joe@debian:~$ echo $[2 + 2] die Zahl 4 aus. Teil von Berechnungen können auch zu substituierende Ausdrücke sein. So führt die folgende Kommandokette zum gleichen Ergebnis: joe@debian:~$ a=2; b=2; echo $[ $a + $b ] Alternativ kann das eingebaute Kommando let zum Ausführen von Berechnungen benutzt werden. Ein Beispiel für Kommandosubstitution innerhalb von Berechnungen wäre folgende Anweisung: joe@debian:~$ echo $[ ‘cat diplom | wc - -lines‘ / 60 ]" Seiten" Hier wird zunächst der Ausdruck ‘cat diplom | wc - -lines‘ aufgelöst, indem das Kommando ausgeführt wird. Das Programm wc gibt mit der Option - -lines die Anzahl der gelesenen Zeilen aus. Das Ergebnis (die Anzahl der Zeilen in der Datei diplom) wird danach durch die Zahl 60 geteilt, und an die daraus resultierende Zeichenkette wird nach einem Leerzeichen das Wort Seiten angehängt. Berechnungen mit der Bash sind auf ganzzahlige Werte zwischen -2147483648 und 2147483647 begrenzt. Gerundet wird nicht. Die Ursache ist, dass die Bash Zahlen intern als vorzeichenbehaftete 32-Bit-Integerwerte speichert. Wenn bei Benutzung der Shell genauere Berechnungen nötig sind, ist das Programm bc (basic calculator) zu empfehlen, auf das jedoch nicht weiter eingegangen werden soll. Es wird in unserem zweiten Debian-Buch erläutert. Die Bash kennt die üblichen arithmetischen Operatoren + (Addition), - (Subtraktion), * (Multiplikation), / (Division) und % (modulo, Divisionsrest), Darüber hinaus sind weitere Funktionen wie logisches und bitweises NICHT, UND und ODER sowie Wertevergleiche (größer als, kleiner als usw.) möglich. Die Funktionen sind in der Info-Dokumentation zu der Bash dokumentiert. Feldvariable Die Bash erlaubt es, eindimensionale Feldvariable (Array) zu definieren. Feldvariable sind Variable, in denen mehrere Werte desselben Typs hintereinander gespeichert sind. Um einen Wert an einer bestimmten Stelle einer Feldvariablen zu speichern
2.7 Kommandointerpreter (Bash)
89
oder ihn von dort zu lesen, ist die Stelle durch einen Index (Hausnummer) anzugeben. Der Index wird in eckigen Klammern hinter der Variablen angegeben. Feldvariable werden – wie gewöhnliche Variable – in dem Moment erzeugt, in dem sie das erste Mal benutzt werden. Wenn eine Variable, die bisher keine Feldvariable war, als Feldvariable angesprochen wird, wird ihr ursprünglicher Wert dem ersten Element zugewiesen. Das erste Element hat den Index 0, wie in der Programmiersprache C üblich. Um Feldern von Feldvariablen Werte zuzuweisen, ist folgende Syntax zu verwenden: Name[Index]=Wert Dadurch wird der Feldvariablen Name an der mit Index bezeichneten Stelle der Wert Wert zugewiesen. Beispiel: joe@debian:~$ a[100]="Feld 100 der Variablen a" Die Belegung aller Felder einer Variablen gleichzeitig geschieht, indem die einzelnen Werte in runden Klammern durch Leerzeichen getrennt angegeben werden. Dadurch werden alle vorherigen Felder der Variablen gelöscht. Beispielsweise definiert das Kommando: joe@debian:~$ Dinge=(Auto Buch Pflanze Viertele) die Feldvariable Dinge und weist den ersten vier Feldern die angegebenen Werte zu. Durch eine Anweisung in dieser Form werden alle bisherigen Felder der Variablen gelöscht. Eine etwas andere Syntax ist notwendig, um Werte aus Feldvariablen zu verwenden. Das folgende Beispiel gibt den Wert des Elements mit dem Index 100 der Feldvariablen a aus: joe@debian:~$ echo ${a[100]} Feldvariablen müssen nach dem Dollarzeichen in geschweifte Klammern gesetzt werden. Der Index kann auch in Form einer Variablen oder eines arithmetischen Ausdrucks angegeben werden, dessen Ergebnis jedoch auf jeden Fall größer oder gleich 0 sein muss. Beispiel: joe@debian:~$ i=100; echo ${a[$i]} Um der Feldvariablen menschen in der durch die Variable i bezeichneten Stelle abzüglich zwei den Wert der Feldvariablen namen in der durch die Variablen i bezeichneten Stelle zuzuweisen, ist folgendes Kommando einzugeben: joe@debian:~$ menschen[$i-2]=${namen[$i]} Einige Shellvariable sind Feldvariable, so beispielsweise BASH_VERSINFO und DIRSTACK, siehe Abschnitt 2.7.5 Benannte Variable auf Seite 77. #!/bin/ksh # Shellscript primscript zur Berechnung von Primzahlen # Korn-Shell erforderlich (apt-get install ksh)
90
2 Linux/UNIX
typeset typeset typeset typeset typeset
-i -i -i -i -i
ende=1000 z=5 i=1 p[500] n=2
# # # # #
groesste Zahl, max. 3600 aktuelle Zahl Index von p Array der Primzahlen, max. 511 Anzahl der Primzahlen
p[0]=2; p[1]=3
# die ersten Primzahlen
while [ z -le ende ]
# die [] muessen von Leerzeichen # umgeben sein (Alias fuer test)
do if [ z%p[i] -eq 0 ] # z teilbar then z=z+2 i=1 else # z nicht teilbar if [ p[i]*p[i] -le z ] then i=i+1 else # Primzahl gefunden p[n]=z; n=n+1 z=z+2 i=1 fi fi done i=0 while [ i -lt n ] do print ${p[i]} i=i+1 done
# Ausgabe des Arrays
echo Anzahl: $n Quelle 2.3 : Shellskript zur Berechnung von Primzahlen
Quelle 2.3 zeigt ein Shellskript für eine untypische Aufgabe, die Berechnung von Primzahlen. Zuerst werden die Variablen deklariert und initialisiert, ebenso bekommt das Array der Primzahlen seine ersten beiden Werte. In einer while-Schleife wird jede weitere ungerade Zahl darauf hin untersucht, ob sie durch die bereits bekannten Primzahlen ohne Rest teilbar ist (Modulo-Operator). Falls nicht, haben wir eine neue Primzahl gefunden, die dem Array hinzugefügt wird. Nachdem wir an der Obergrenze angelangt sind, verlassen wir die Schleife und geben das Array aus. Ein C- oder Fortran-Programm erledigt solche Aufgaben deutlich schneller und verfügt über einen größeren Zahlenbereich. In Quelle 2.18 auf Seite 114 zeigen wir das Skript auf Perl umgeschrieben.
2.7 Kommandointerpreter (Bash)
91
Klammererweiterung Der Mechanismus der Klammererweiterung (brace expansion) erlaubt, eine Reihe von ähnlichen Zeichenfolgen relativ schnell zu erzeugen. Dazu ist in geschweiften Klammern eine Anzahl von Zeichenketten zu benennen, die durch Kommas voneinander getrennt werden. Im Unterschied zu der Expansion von Dateinamen mit Metazeichen wird hierbei nicht geprüft, ob die aus dem betreffenden Ausdruck erzeugbaren Zeichenketten als Dateinamen existieren. Das Kommando: joe@debian:~$ echo /usr/share/doc/{aa,bb} kombiniert die Zeichenketten aa und bb mit der vorangestellten Zeichenkette und gibt beide daraus erzeugbaren Zeichenketten aus. Möchten man im Verzeichnis /usr/local/doc die Verzeichnisse hosts, tools und adms anlegen, so geschieht dies mit folgendem Kommando: joe@debian:~$ mkdir /usr/local/doc/{hosts,tools,adms} Wenn mehrere Klammererweiterungen in einem Ausdruck vorkommen, werden die darin enthaltenen Ausdrücke miteinander kombiniert. Dies zeigt folgendes Kommando: joe@debian:~$ echo /usr/share/doc/{1,2}/{a,b,c} Auf diese Art lässt sich eine große Zahl möglicher Kombinationen von Zeichenketten schnell angeben. Vorteilhaft ist, dass die Ausdrücke in den geschweiften Klammern auch Metazeichen enthalten dürfen. Diese werden wie üblich expandiert, nachdem die Kombinationen aus den Zeichenketten gebildet worden sind. So gibt das Kommando: joe@debian:~$ echo /usr/share/doc/{a*,b*} die Namen aller Dateien im Verzeichnis /usr/share/doc aus, deren Namen mit einem kleinen a oder b beginnen. Will man aus den Unterverzeichnissen von /usr/share/doc, deren Namen mit a oder b beginnen, alle Dateien, deren Namen mit README oder NEWS beginnen, in das Verzeichnis docs kopieren, so erledigt dies folgendes Kommando: joe@debian:~$ cp /usr/share/doc/{a*,b*}/{README*,NEWS*} docs Bei der Expansion werden zunächst vier Zeichenketten gebildet, nämlich: 1. 2. 3. 4.
/usr/share/doc/a*/README* /usr/share/doc/a*/NEWS* /usr/share/doc/b*/README* /usr/share/doc/b*/NEWS* Daraufhin werden die Metazeichen expandiert. Dies geschieht für jede der vier Zeichenketten einzeln und nacheinander. Die Expansion geschieht nur dann, falls es tatsächlich passende Dateinamen gibt. Wenn beispielsweise aus der Zeichenkette /usr/share/doc/b*/NEWS* keine Dateinamen gebildet werden können, findet für diese Zeichenkette keine Expansion statt. Sie bleibt unverändert.
92
2 Linux/UNIX
Bedingte Variablenexpansion Die Bash bietet Mechanismen, die es erlauben, Variable nur unter bestimmten Bedingungen zu expandieren. Dadurch ist es möglich, Werte beispielsweise nur dann zu interpretieren, wenn diese auch gesetzt sind. Die Konstruktionen werden mit einem Dollarzeichen eingeleitet und dann in geschweifte Klammern gesetzt. Es stehen die im Folgenden genannten Konstrukte zur Verfügung. Darin angegebene Zeichenketten können normale Zeichenketten oder Variable sein, die dann vor der Auswertung des jeweiligen Ausdrucks ausgewertet werden. •
•
•
•
•
•
${Variable} Entspricht dem Wert von Variable, ist gleichbedeutend mit $Variable. Die Schreibweise ist jedoch geeignet, nachfolgende Zeichen von dem Namen der betreffenden Variablen abzugrenzen. Beispiel: während die Zeichenkette $ab durch den Inhalt der Variablen ab ersetzt werden würde, ist die Zeichenkette ${a}b gleichbedeutend mit dem Inhalt der Variablen a und dem unmittelbar angehängten Zeichen b. ${Variable:-Zeichenkette} Wenn die Variable Variable existiert und einen Wert hat, ist der Ausdruck gleich dem Wert von Variable, sonst gleich dem Wert von Zeichenkette. ${Variable:=Zeichenkette} Wenn die Variable Variable existiert und einen Wert hat, ist der Ausdruck gleich dem Wert von Variable. Wenn nicht, wird der Wert von Zeichenkette Variable zugeordnet und der Ausdruck ist gleich Zeichenkette. ${Variable:?Zeichenkette} Wenn die Variable Variable existiert und einen Wert hat, ist der Ausdruck gleich dem Wert von Variable. Wenn nicht, wird die mit Zeichenkette angegebene Zeichenkette auf die Standardfehlerausgabe gegeben und die Ausführung weiterer Kommandos abgebrochen. Falls die Shell nicht interaktiv ist, wird sie beendet. Mit dieser Konstruktion lässt sich prüfen, ob alle notwendigen Parameter für die Ausführung eines Shellskripts vorliegen. ${Variable:+Zeichenkette} Wenn die Variable Variable existiert und einen Wert hat, ist der Ausdruck gleich der mit Zeichenkette angegebenen Zeichenkette. Wenn Variable nicht existiert oder keinen Wert hat, ist der Ausdruck gleich der leeren Zeichenkette. ${Variable:Anfangspunkt[:Endpunkt]} Der Ausdruck ist gleich der Teilzeichenkette der in Variable gespeicherten Zeichenkette, die mit dem mit Anfangspunkt angegebenen Zeichen anfängt und mit dem optional mit Endpunkt (ohne eckige Klammern) angegebenen Zeichen aufhört. Bei Anfangspunkt und Endpunkt muss es sich um Ausdrücke handeln, die Zahlen entsprechen. Wenn Endpunkt nicht angegeben ist, ist der Ausdruck gleich der Zeichenkette aus Variable von Anfangspunkt bis zum Ende. Wenn Anfangspunkt einer negativen Zahl entspricht, wird der Anfangspunkt vom Ende der Zeichenkette aus Variable an rückwärts ermittelt. Wenn die Variable @ ist, entspricht das Ergebnis den Shell-Argumenten vom Anfangspunkt-en Argument bis zum Endpunkt-en Argument. Wenn Variable eine Feldvaria-
2.7 Kommandointerpreter (Bash)
•
93
ble mit dem Index @ oder * ist, entspricht der Ausdruck dem Inhalt von dem Anfangspunkt-en Feld bis zum Endpunkt. ${#Variable} Der Ausdruck entspricht der Anzahl der Zeichen, aus denen die in Variable gespeicherte Zeichenkette besteht. Wenn Variable gleich @ oder * ist, entspricht der Ausdruck der Anzahl der Argumente der Shell. Wenn Variable eine Feldvariable mit dem Index @ oder * ist, entspricht der Ausdruck der Anzahl der Elemente dieser Feldvariablen.
2.7.7 Shellskripte Allgemeine Bemerkungen zur Skripterstellung Am Anfang des Kapitels wurde gezeigt, wie sich Shellkommandos in eine Textdatei schreiben lassen und diese dann wie ein Programm ausgeführt werden kann, siehe Quelle 2.2 auf Seite 48. Dabei sind zwei Dinge zu beachten: •
Zu Beginn der ersten Zeile sollte sich folgender Eintrag befinden: #!/bin/bash Durch diese Zeile – Shebang genannt – wird das Betriebssystem angewiesen, das Skript durch die Bash auszuführen. In vielen Shell-Skripten findet man in der ersten Zeile auch den folgenden Eintrag: #!/bin/sh
•
Dies bedeutet, dass das Skript von der Bourne-Shell ausgeführt werden soll. Unter Debian GNU/Linux ist /bin/sh ein symbolischer Link nach /bin/bash. Es wird mit beiden Zeilen dasselbe Programm aufgerufen. Allerdings verhält sich die Bash etwas anders, wenn sie über den Namen /bin/sh aufgerufen wird. Hierdurch wird die Kompatibilität zur Bourne-Shell gewährleistet. Die Datei muss für denjenigen, der sie benutzen will, les- und ausführbar sein. Dies wird mit dem Kommando chmod eingestellt. Die Kennung .sh bei Shellskripten ist nicht zwingend, die Shell interessiert sich nicht dafür.
Das Arbeitsverzeichnis, in dem sich das Shellskript befindet, ist nicht immer im Suchpfad für ausführbare Dateien enthalten (PATH-Variable). Das Skript wird daher nicht in jedem Fall gefunden. Dies lässt sich ändern, indem das Arbeitsverzeichnis mit in die Werteliste der Variablen PATH aufgenommen wird oder das Skript mit seinem Verzeichnisnamen aufgerufen wird. Das Kommando: joe@debian:~$ PATH=".:$PATH" fügt das durch einen Punkt dargestellte Arbeitsverzeichnis vor allen anderen Verzeichnissen in den Suchpfad für ausführbare Dateien ein8 . Solange Sie Ihren eigenen Programmen Namen geben, die nicht bereits im System vorkommen, spielt es 8
Als Verwalter können Sie Skripte auch in ein Verzeichnis kopieren, das im systemweiten Suchpfad für Programme enthalten ist. Oft verwendet man hierzu das Verzeichnis /usr/local/bin.
94
2 Linux/UNIX
keine Rolle, ob Sie den Punkt im Pfad voranstellen oder anhängen. Andernfalls wird das Programm ausgeführt, das als erstes gefunden wird. Um ein Programm im Arbeitsverzeichnis mit Verzeichnisangabe (relativem Pfad) aufzurufen, stellt man dem Namen der Programmdatei die Zeichenkette ./ voran: joe@debian:~$ ./myscript Damit wird in jedem Fall die Datei myscript aus dem Arbeitsverzeichnis ausgeführt. Kommentare Leere Zeilen und Zeilen, die mit einem Doppelkreuz beginnen, werden von der Bash nicht beachtet. Das Doppelkreuz darf auch irgendwo in einer Zeile, nach gültigen Anweisungen, stehen und gilt dann für den Rest der Zeile. Dadurch ist es möglich, Kommentare in Skripte zu schreiben und einzelne Kommandos in sinnvoll zusammenhängenden Gruppen anzuordnen. Hiervon sollte man ausgiebig Gebrauch machen. Am Tag, an dem ein Skript geschrieben wird, ist noch klar, wozu die einzelnen Anweisungen gut sind. Zwei Wochen oder zwei Jahre später sieht das anders aus. Variable Zur weiteren Steigerung der Übersichtlichkeit ist zu empfehlen, Variablennamen zu wählen, die aussagekräftig sind. Der Name z lässt nicht unbedingt erkennen, was in dieser Variablen gespeichert wird, wohingegen der Name eingabedatei dies eher ermöglicht. Die Autoren empfinden es weiter als sinnvoll, Variablen in umfangreichen Skripten zu Beginn der Skripte zu deklarieren und ihnen Werte zuzuweisen (initialisieren). Dadurch kann auf einen Blick geprüft werden, ob ein bestimmter Name schon vergeben ist. Die vordefinierten Positionsvariablen stehen selbstverständlich in Shellskripten zur Verfügung, ebenso Umgebungsvariable. Ablaufsteuerung (Schleifen) Jedes Skript, das mehr als nur eine Folge von Kommandos ausführt, benötigt eine Ablaufsteuerung (E: flow control, F: structure de contrôle). Das sind Kommandos, die bestimmen, ob eine Aktion ausgeführt wird, die angeben, wie bei einem Fehler verfahren wird, oder die festlegen, wie oft eine Aktion in einer Schleife (E: loop, F: boucle) ausgeführt wird. Die Bash bietet dazu eine Reihe eingebauter Kommandos, die ähnlich auch in anderen Sprachen bekannt sind. Alle Kommandos zur Ablaufsteuerung können sowohl in der Kommandozeile als auch in Skripten benutzt und beliebig verschachtelt werden. Die folgende Übersicht ist ähnlich aufgebaut wie die Kommandoübersicht im Anhang ab Seite 937. Auch hier gilt, dass Ausdrücke in eckigen Klammern optional (ohne eckige Klammern) zu benutzen sind. Die meisten Konstrukte zur Ablaufsteuerung setzen sich aus mehreren Schlüsselwörtern zusammen. Dem Wiederholen von Kommandos für eine Anzahl von Zeichenketten dient die for-Schleife:
2.7 Kommandointerpreter (Bash)
95
for Variable [in Zeichenkette [Zeichenkette ...]]; do Kommando; [Kommando; ...] done; Das Kommando führt das oder die mit Kommando angegebene(n) Kommando(s) für jede mit Zeichenkette angegebene Zeichenkette einmal aus. Dabei wird der mit Variable angegebenen Variablen jeweils die Zeichenkette zugewiesen, für welche die Kommandos gerade ausgeführt werden. Das Kommando: joe@debian:~$ for i in tee bier wein; do echo $i; done; gibt hintereinander die Wörter tee, bier und wein aus. Ist keine Zeichenkette angegeben, werden stattdessen die Positionsvariablen verwendet. Folgende Anweisungen bedeuten das Gleiche: joe@debian:~$ for i; do echo $i; done joe@debian:~$ for i in $*; do echo $i; done Beide bewirken, dass alle Positionsparameter hintereinander ausgegeben werden. Ein Skript als Beispiel einer Anwendung der for-Schleife: #!/bin/bash for verzeichnis in briefe texte musik grafik do cd $verzeichnis mkdir -p backup mv *~ *.bak *.old backup cd .. done; Quelle 2.4 : Shellskript backup.sh als Beispiel einer for-Schleife
Das Skript wechselt nacheinander in die Verzeichnisse briefe, texte, musik und grafik, legt dort jeweils ein Unterverzeichnis mit dem Namen backup an – sofern es noch nicht existiert – und verschiebt alle Dateien mit den Kennungen ~, .bak oder .old in das Unterverzeichnis. Nach jedem Durchlauf wird wieder zurück in das Ausgangsverzeichnis gewechselt. Wie man sieht, unterscheiden sich die for-Schleifen der Shell und der Programmiersprache C erheblich in ihrer Bedeutung. Die while-Schleife wiederholt eine Aktion, solange Kommandos erfolgreich ausgeführt werden: while Testkommando; [Testkommando; ...]; do Kommando; [Kommando; ...] done; Zunächst werden die mit Testkommando angegebenen Kommandos ausgeführt. Wenn das letzte Kommando einen Rückgabewert 0 (Erfolg) hat, werden die mit Kommando angegebenen Kommandos ausgeführt. Danach werden wieder die mit Testkommando angegebenen Kommandos ausgeführt. Die Schleife wird so lange
96
2 Linux/UNIX
wiederholt, bis das letzte Testkommando einen von 0 abweichenden Rückgabewert (Misserfolg) liefert. #!/bin/bash # Definition einer Funktion function frage { while true do echo -n ’Weitermachen? ’ read antwort || return 1 case \$antwort in j|ja|y|yes|oui) return 0;; n|nein|no|non) return 1;; *) echo ’Mit j oder n antworten’;; esac done } # Anwendung der Funktion frage while frage do date done
# oder etwas Sinnvolleres
Quelle 2.5 : Shellskript frage.sh als Beispiel einer while-Schleife
Im Skript wird eine Funktion definiert, die eine while-Schleife enthält, deren Bedingung immer true ist. Das wäre eine ewige Schleife, wenn sie nicht mittels return verlassen würde. Die Funktion schreibt eine Frage auf den Bildschirm und gibt je nach Antwort eine 0 oder 1 zurück. Im Hauptteil des Skripts, der Anwendung der Funktion, steht eine zweite while-Schleife, die die Funktion aufruft, ihr Ergebnis als Bedingung auswertet und das externe Kommando date aufruft oder abbricht. Danach ist das Skript beendet. Die until-Schleife wiederholt eine Aktion, solange Kommandos nicht erfolgreich ausgeführt werden: until Testkommando; [Testkommando; ...]; do Kommando; [Kommando; ...] done; Dieses Kommando gleicht dem while-Kommando mit dem Unterschied, dass die mit Kommando angegebenen Kommandos so lange ausgeführt werden, wie das letzte Testkommando einen Rückgabewert ungleich 0 liefert, also fehlschlägt. #!/bin/bash until ping -c 1 buchhaltung &> /dev/null
2.7 Kommandointerpreter (Bash)
97
do echo "Rechner buchhaltung nicht erreichbar" sleep 5 done telnet buchhaltung Quelle 2.6 : Shellskript erreichbar.sh als Beispiel einer until-Schleife
Das Skript untersucht mit dem Kommando ping, ob der Rechner buchhaltung zu erreichen ist. Die Ausgabe von ping interessiert nicht, weswegen sie nach /dev/null umgeleitet wird. Wenn der Test fehlschlägt, weil der Rechner schweigt, wird eine Meldung ausgegeben und fünf Sekunden gewartet. Danach wird der Test wiederholt. Sobald der Rechner buchhaltung antwortet und ping den Rückgabewert 0 liefert, wird eine Verbindung mittels telnet aufgebaut. Dem menügesteuertes Ausführen von Aktionen dient das Kommando select: select Variable [in Zeichenkette [Zeichenkette ...]]; do Kommando; [Kommando; ...] done; Dieses Kommando ist syntaktisch ähnlich aufgebaut wie das for-Kommando. Es wird ein Menü dargestellt, in dem alle mit Zeichenkette angegebenen Zeichenketten aufgeführt sind. Jeder Zeichenkette wird dabei eine Zahl zugeordnet. Zusätzlich erscheint eine Aufforderung zur Eingabe einer Zahl. Das Erscheinungsbild des Prompts wird durch die eingebaute Variable PS3 festgelegt. Wenn mit Zeichenkette keine Zeichenkette angegeben ist, werden wie beim Kommando for stattdessen die Positionsvariablen verwendet. Nachdem der Benutzer eine Zahl ausgewählt hat, werden die mit Kommando angegebenen Kommandos ausgeführt. Der Wert der mit Variable bezeichneten Variablen ist dabei gleich der Zeichenkette, die der Zahl entspricht, welche vom Benutzer ausgewählt wurde. Nach Ausführung aller Kommandos wird das Menü erneut dargestellt, und es kann wieder eine Auswahl getroffen werden. Wenn eine ungültige Zahl eingegeben wurde, ist Variable leer. Skripte, die das Kommando verwenden, sollten dies testen und für eine ungültige Auswahl eine spezielle Aktion vorsehen. Die Schleife wird mit dem Kommando break verlassen. #!/bin/bash PS3="Wahlen Sie, was angezeigt werden soll: " select auswahl in Speicher Platten Benutzer Zeit Ende echo do echo case $auswahl in Speicher ) free ;; Platten ) df ;; Benutzer ) who ;; Zeit ) uptime ;;
98
2 Linux/UNIX
Ende ) * ) esac done
break ;; echo ungueltig!
echo "Bis bald..." Quelle 2.7 : Shellskript auswahl.sh als Beispiel einer Auswahl mittels select
Das Skript stellt eine interaktive Schnittstelle zu Werkzeugen zur Systeminformation dar. In der zweiten Zeile wird der Variablen PS3 eine Zeichenkette zugewiesen, die als Prompt für eine Auswahl dient. Dann wird select aufgerufen, wodurch ein Menü dargestellt wird, in dem der Benutzer zwischen den Punkten Arbeitsspeicher, Festplatten usw. auswählen kann. Nach erfolgter Auswahl wird mit echo eine Leerzeile ausgegeben. Danach wird mit dem Kommando case für jede Auswahl ein Programm aufgerufen. Bei der Auswahl von Ende wird die Schleife mit dem Kommando break verlassen. Die letzte Zeile in der caseGruppe gibt eine Fehlermeldung aus, falls eine ungültige Auswahl eingegeben wurde. Nach Beendigen der Schleife verabschiedet sich das Skript. Noch ein Beispiel: #!/bin/bash PS3="Welche Datei loeschen? " select datei in * do rm $datei; break; done; Quelle 2.8 : Shellskript loeschen.sh als Beispiel einer Auswahl mittels select
Die Kommandofolge zeigt die Dateien im Arbeitsverzeichnis an, die dem Metazeichen * entsprechen (alle Dateien, könnte auch ein anderes Muster sein) und bietet die Möglichkeit, eine dieser Dateien auszuwählen. Die ausgewählte Datei wird gelöscht und die Auswahl verlassen. Um eine Schleife sofort zu verlassen, gibt man das Kommando break: break [Tiefe] Mit for, while, until oder select aufgebaute Schleifen werden sofort verlassen, wenn in ihnen das Kommando break ausgeführt wird. Optional kann dem Kommando die Tiefe von ineinander verschachtelten Schleifen übergeben werden. Es werden dann so viele Schleifen gleichzeitig verlassen, wie mit Tiefe angegeben ist. Vorgabewert für Tiefe ist 1. #!/bin/bash for verzeichnis in texte grafiken do cd $verzeichnis || break for datei in liste daten index
2.7 Kommandointerpreter (Bash)
99
rm $datei || break 2 done cd .. done Quelle 2.9 : Shellskript abbruch.sh als Beispiel des Abbruchs einer Schleife mittels break
Das Beispiel zeigt zwei geschachtelte for-Schleifen. In der äußeren Schleife wird eine Anzahl von Verzeichnissen durchlaufen und versucht, in diese Verzeichnisse zu wechseln (cd $verzeichnis). Falls das Kommando fehlschlägt, wird die Schleife sofort mit break verlassen. Anderenfalls wird die innere Schleife gestartet, in der versucht wird, eine Anzahl von Dateien zu löschen. Sobald dies für eine Datei fehlschlägt, werden beide Schleifen verlassen. Mit dem Kommando continue springt man an das Ende einer Schleife: continue [Tiefe] Das Kommando continue führt dazu, dass alle weiteren Kommandos innerhalb einer Schleife nicht mehr ausgeführt werden, sondern sofort an das Ende der Schleife gesprungen wird. Danach wird sie erneut ausgeführt. Auch hier lässt sich optional die Tiefe angeben, wodurch an das Ende der Schleife der entsprechenden Schachtelungstiefe gesprungen wird. Vorgabe für Tiefe ist wieder 1. #!/bin/bash for verzeichnis in texte grafiken do cd $verzeichnis || continue for datei in liste daten index rm $datei || break 2 done cd .. done Quelle 2.10 : Shellskript abbruch2.sh als Beispiel des Abbruchs einer Schleife mittels continue
Diese Abwandlung des vorherigen Beispiels bewirkt im Falle einer nicht erfolgreichen Ausführung des cd-Kommandos, dass die Schleife nicht ganz abgebrochen wird, sondern lediglich an ihr Ende gesprungen wird und daraufhin mit dem nächsten Verzeichnis weitergemacht wird. Bedingtes Ausführen von Kommandos Ein Mittel zur bedingten Ausführung von Kommandos wurde bereits vorgestellt, siehe Abschnitt 2.7.4 Bedingte Ausführung mit den Operatoren && und || auf Seite 73.
100
2 Linux/UNIX
Die Operatoren erlauben, Kommandos nur dann auszuführen, wenn das vorangehende Kommando erfolgreich (&&) oder nicht erfolgreich (||) ausgeführt wurde. Die Kommandos if und case bieten jedoch weit mächtigere Möglichkeiten, in Abhängigkeit von Zuständen bestimmte Kommandos und Kommandofolgen auszuführen. Im Zusammenhang hiermit kommt dem eingebauten Kommando test eine besondere Bedeutung zu. letzes Kommando erfolgreich (rc=0)
if
Kommandokette
then
Kommandokette
Ende
letztes Kommando nicht erfolgreich (rc!=0)
letzes Kommando erfolgreich (rc=0)
elif
Kommandokette
then
Kommandokette
Ende
letztes Kommando nicht erfolgreich (rc!=0)
letzes Kommando erfolgreich (rc=0)
elif
Kommandokette
then
Kommandokette
Ende
letztes Kommando nicht erfolgreich (rc!=0)
else
Kommandokette
Ende
fi; Abb. 2.6: Schema des Skriptablaufs bei Verwendung des if-Kommandos. Die Abkürzung rc steht für Returncode (Rückgabewert).
if Testkommando1; [Testkommando1; ...]; then Kommando1; [Kommando1; ...]; [elif Testkommando2; [Testkommando2; ...]]; then Kommando2; [Kommando2; ...] [elif ...] [else Kommando3; [Kommando3; ...]] fi; Das Kommando if führt die mit Testkommando1 angegebenen Kommandos aus. Wenn der Rückgabewert des letzten Testkommandos 0 ist, werden die unter Kommando1 genannten Kommandos ausgeführt. Abbildung 2.6 verdeutlicht den Ablauf.
2.7 Kommandointerpreter (Bash)
101
Hinter den Kommandos, die ausgeführt werden, wenn die erste Testbedingung zutrifft, kann das Schlüsselwort elif stehen. Dann werden, wenn die erste Testbedingung nicht zutrifft, die hinter elif angegebenen Testkommandos ausgeführt. Wenn das letzte dieser Kommandos den Rückgabewert 0 liefert, werden die Kommandos ausgeführt, die hinter dem nächsten then stehen. Es dürfen mehrere elif ...then-Paare hintereinander vorkommen. Dann werden so lange die verschiedenen hinter den elif-Wörtern stehenden Testkommandos ausgeführt, bis das letzte Kommando eines der Testkommandos den Rückgabewert 0 liefert. Am Ende der Konstruktion kann das Schlüsselwort else stehen. Es bewirkt, dass die hinter else stehenden Kommandos ausgeführt werden, wenn keines der vorhergegangenen, hinter if oder elif aufgeführten jeweils letzten Testkommandos einen Rückgabewert 0 hat. Die Folge von bedingten Anweisungen wird mit dem Wort fi abgeschlossen. Hier ein Beispiel für die Anwendung bedingter Anweisungen: #!/bin/bash # Variable initialisieren rechner="verwaltung abrechnung personal edv" tot=0; totnamen="" mailserver="mail.provider.de" email="[email protected]" faxnummer="0049111222333" log=/var/log/logfile # Versuchen, die Rechner zu erreichen for i in $rechner do if ping -c 1 $i &> /dev/null then echo "Rechner $i ok." » $log else tot=$[$tot+1] totnamen=$totnamen" "$i echo "Rechner $i nicht ok." » $log fi done # Wenn Rechner unerreichbar, versuchen, # den Verwalter zu erreichen. if [ $tot -ne 0 ] then if ping -c 1 $mailserver &> /dev/null then echo $totnamen | mail -s "Rechner ausgefallen" $email echo "Email gesendet" » $log elif echo "ausgefallene Rechner: "$totnamen | sendfax -P 0 -n -d $faxnummer &> /dev/null then echo "Fax gesendet" » $log else echo "Benachrichtigung unmoeglich" » $log fi fi Quelle 2.11 : Shellskript bedingt.sh als Beispiel für bedingte Anweisungen
102
2 Linux/UNIX
Das Skript 2.11 dient zur Überwachung der Erreichbarkeit einiger Rechner, die in der Variablen rechner aufgeführt werden. Im ersten Teil werden die Variablen deklariert und mit Werten versehen. Im zweiten Teil wird mit einer for-Schleife versucht, die Rechner mit dem Kommando ping zu erreichen. Dem for-Kommando werden dazu die Namen der zu überprüfenden Rechner mit der Variablen rechner übergeben. Das darunter stehende if-Kommando ruft das Programm ping auf, das den Rückgabewert 0 liefert, wenn der jeweilige Rechner erreicht wird. In diesem Fall wird in die Datei, deren Namen in der Variablen log gespeichert ist, eine Erfolgsmeldung geschrieben. Wenn ping einen von 0 abweichenden Rückgabewert liefert, also der betreffende Rechner nicht zu erreichen ist, wird zu der Variablen tot der Wert eins hinzuaddiert. Weil die Variable zu Beginn des Skriptes mit dem Wert 0 initialisiert war, enthält sie nach Beendigung der Schleife die Anzahl der nicht erreichten Rechnerc. Außerdem werden der Variablen totnamen der Name des nicht erreichbaren Rechners angehängt und ein Eintrag in die Logdatei geschrieben. Im dritten Teil wird anhand der Variablen tot geprüft, ob es nicht erreichbare Rechner gegeben hat. Die eckigen Klammern sind eine vereinfachte Schreibweise für das Kommando test. Um die eckigen Klammern müssen Zwischenräume stehen, da es Kommandos sind, keine Operatoren. Falls es unerreichbare Rechner gab, soll der Verwalter benachrichtigt werden. Zu dem Zweck wird zunächst mit dem gleichen Verfahren geprüft, ob wenigstens der Mailserver zu erreichen ist. Wenn der Test erfolgreich ist, wird mit dem Kommando mail eine Mail an die in der Variablen email gespeicherte Anschrift gesendet, welche die angegebene BetreffZeile enthält. Das Programm mail liest den Inhalt der zu sendenden Mail von der Standardeingabe. Es wird die Liste der Namen nicht erreichbarer Rechner gesendet. Nachdem die Mail abgeschickt ist, wird ein entsprechender Eintrag in die Logdatei geschrieben. Wenn der Mailserver jedoch nicht erreicht werden konnte (Rückgabewert ungleich 0), wird versucht, ein Fax an die in der Variablen faxnummer gespeicherte Faxnummer zu senden. Hierzu wird das Programm sendfax aus dem Debian-Paket hylafax-client benutzt. Wenn der Rückgabewert von sendfax 0 ist, wird die erfolgreiche Absendung des Faxes in der Logdatei vermerkt. Sollte jedoch auch der Versuch, ein Fax zu senden, fehlschlagen, wird in der Logdatei vermerkt, dass es nicht möglich war, den Verwalter zu benachrichtigen. Testen von Bedingungen test Ausdruck oder [ Ausdruck ] test überprüft, ob die mit Ausdruck bezeichnete Bedingung wahr oder falsch ist, und liefert den Wert 0 zurück, falls die Bedingung zutrifft. Anderenfalls wird der Wert eins zurückgeliefert. Das Kommando kann immer dann eingesetzt werden, wenn bei der bedingten Ausführung von Kommandos nicht der Rückgabewert eines
2.7 Kommandointerpreter (Bash)
103
Programms beurteilt werden, sondern geprüft werden soll, ob bestimmte Dateien existieren oder ob Variablen bestimmte Werte haben. Die Entscheidung ist abhängig vom Rückgabewert von test, der seinerseits abhängig vom Zutreffen oder NichtZutreffen bestimmter Bedingungen ist. Es gibt zwei unterschiedliche Schreibweisen für dieses Kommando. In der ersten Form wird test wie jedes andere Kommando aufgerufen. Als Parameter werden ihm, wie unten beschrieben, die zu testenden Ausdrücke übergeben. In der zweiten Form wird an Stelle des Wortes test eine linke eckige Klammer geschrieben, die – gefolgt von dem zu testenden Ausdruck – von einer rechten eckigen Klammer geschlossen wird. Die eckigen Klammern müssen als Kommandos von Leerzeichen eingerahmt werden. Die zweite Schreibweise erhöht die Lesbarkeit des Kommandos in Skripten. Die beiden Kommandos: joe@debian:~$ test -e diplom || echo "Arbeit weg" joe@debian:~$ [ -e diplom ] || echo "Arbeit weg" sind identisch in ihrer Wirkung. Die Bedingungen bedeuten: • • • • • • • • • • • • • • •
-e Datei Trifft zu, wenn Datei existiert. -x Datei Trifft zu, wenn Datei existiert und ausführbar ist. -d Datei Trifft zu, wenn Datei existiert und ein Verzeichnis ist. -r Datei Trifft zu, wenn Datei existiert und gelesen werden kann. -w Datei Trifft zu, wenn Datei existiert und beschreibbar ist. Zeichenkette1 = Zeichenkette2 Die Zeichenketten sind gleich. Ausdruck1 -eq Ausdruck2 Die Ausdrücke haben den gleichen numerischen Wert. Ausdruck1 -lt Ausdruck2 Der linke Ausdruck ist numerisch kleiner als der rechte Ausdruck. Ausdruck1 -gt Ausdruck2 Der linke Ausdruck ist numerisch größer als der rechte Ausdruck. Ausdruck1 -ne Ausdruck2 Die Ausdrücke sind numerisch ungleich. -z Zeichenkette Trifft zu, wenn die Zeichenkette leer ist. Dadurch lässt sich prüfen, ob eine Variable einen Wert hat. -n Zeichenkette Trifft zu, wenn die Zeichenkette nicht leer ist. ! Ausdruck Trifft zu, wenn Ausdruck nicht zutrifft (logisches NOT). Ausdruck1 -o Ausdruck2 Trifft dann zu, wenn Ausdruck1 oder Ausdruck2 oder beide zutreffen (logisches OR). Ausdruck1 -a Ausdruck2 Trifft dann zu, wenn Ausdruck1 und Ausdruck2 zutreffen (logisches AND).
Im obigen Shellskript zur Überprüfung der Erreichbarkeit von Rechnern wurde mit dem Kommando test in der Zeile if [ $tot -ne 0 ] getestet, ob die Variable tot einen von 0 abweichenden Wert hat. Dies ist in dem Skript genau dann der Fall, wenn mindestens ein Rechner nicht zu erreichen war. Nun möchte man das Skript vielleicht dahingehend ändern, dass der Verwalter erst dann benachrichtigt
104
2 Linux/UNIX
wird, wenn mehr als ein Rechner nicht zu erreichen ist. Die Zeile ist dann folgendermaßen zu ändern: if [ $tot -gt 1 ] Nun liefert test nur dann den Rückgabewert 0, wenn die Variable tot einen Wert größer als eins enthält. Mit test und while lassen sich auch Schleifen realisieren, die nur einige Male durchlaufen werden sollen: #!/bin/bash durchlauf=0 anzahl=10 while [ $anzahl -ne $durchlauf ] do echo $durchlauf durchlauf=$[$durchlauf+1] done Quelle 2.12 : Shellskript durchlauf.sh als Beispiel für einen Schleifenzähler mittels test bzw. eckigen Klammern
Hier wird als Test-Kommando für die while-Schleife das Kommando test in Form der eckigen Klammern benutzt, das prüft, ob die Variablen anzahl und durchlauf (Schleifenzähler) einen unterschiedlichen numerischen Wert haben. Falls dies zutrifft, liefert test den Rückgabewert 0, und die Schleife wird durchlaufen. Innerhalb der Schleife wird zunächst der Wert der Variablen durchlauf ausgegeben und ihr Wert dann um eins erhöht. Die Folge ist, dass nach zehn Durchläufen ihr Wert gleich 10 ist und dann die Test-Bedingung (anzahl und durchlauf unterscheiden sich) nicht mehr zutrifft, woraufhin test einen von 0 abweichenden Rückgabewert liefert und die Schleife verlassen wird. Ein weiterer Anwendungsbereich von test liegt darin zu überprüfen, ob Dateien existieren und bestimmte Eigenschaften haben. So führt es zu Fehlern, wenn Programme aufgerufen werden, die nicht vorhanden sind, oder aus Dateien gelesen werden soll, für die keine Leseberechtigung besteht. Solche Fehler können durch Test-Anweisungen vor Aufruf des eigentlichen Programms vermieden werden. Das folgende Skript ruft das in Wirklichkeit nicht vorhandene Programm /usr/local/bin/shuffle auf. Das Programm liest Daten von der Standardeingabe und schreibt seine Ergebnisse auf die Standardausgabe. Weil das Programm im Falle eines Fehlers keine Meldungen ausgibt, die auf die Ursache des Fehlers schließen lassen, wurde das Skript geschrieben. Es testet zunächst die notwendigen Bedingungen zur erfolgreichen Ausführung von /usr/local/bin/shuffle, gibt entsprechende Fehlermeldungen aus, falls die Bedingungen nicht erfüllt sind, und ruft das Programm nur dann auf, wenn alle Bedingungen erfüllt sind: #!/bin/bash
2.7 Kommandointerpreter (Bash)
105
programm=/usr/local/bin/shuffle eingabe=/tmp/shuffledata ausgabe=/tmp/shuffle.out if [ ! -x $programm ]; then echo "Fehler: Programm nicht ausfuhrbar." exit 1; fi; if [ ! -r $eingabe ]; then echo "Fehler: Kann Daten nicht lesen." exit 1; fi; if [ ! -w $ausgabe ]; then echo "Fehler: Ausgabe nicht beschreibbar." exit 1; fi; # Alles stimmt, wir rufen das Programm auf. cat $eingabe | $programm » $ausgabe Quelle 2.13 : Shellskript preshuffle.sh als Beispiel eines Testes für die Ausführbarkeit eines Programmes
In dem Skript werden zunächst Variable definiert, die den Namen der Programmdatei sowie die Namen von Ein- und Ausgabedatei enthalten. Dann wird getestet, ob die Programmdatei vorhanden und ausführbar ist (-x), ob die Eingabedatei vorhanden und lesbar ist (-r) und ob die Ausgabedatei vorhanden und beschreibbar ist (-w). Wenn eine der Bedingungen nicht erfüllt ist, wird das Skript mit exit abgebrochen. Dem Kommando exit wird dabei die Zahl 1 als Parameter übergeben. Dadurch wird bewirkt, dass der Rückgabewert des Skriptes 1 ist. Ein anderer Prozess oder ein anderes Skript kann anhand des Rückgabewertes dieses Skriptes prüfen, ob es erfolgreich ausgeführt wurde. Bedingtes Ausführen von Kommandos in Abhängigkeit von Variablen (case) case Variable in Zeichenkette1 [ | Zeichenkette1 ... ] ) Kommando1 [; Kommando1 ...] ;; [ Zeichenkette2 [ | Zeichenkette2 ... ] ) Kommando2 [; Kommando2 ]] ;; [...] esac Das Kommando case ermöglicht, in Abhängigkeit vom Wert einer Variablen verschiedene Aktionen auszuführen. Ein Beispiel dafür wurde bereits zusammen mit dem Kommando select vorgestellt. Zunächst wird der in der mit Variable bezeichneten Variablen gespeicherte Wert mit der Zeichenkette Zeichenkette1 verglichen. Hier kann an Stelle einer einfachen Zeichenkette auch eine Variable oder
106
2 Linux/UNIX
ein Kommando in einfachen rückwärtsgerichteten Anführungsstrichen stehen (Kommandosubstitution). Dann wird statt der Zeichenkette mit der Ausgabe dieses Kommandos oder dem Wert der Variablen verglichen. Die Zeichenkette darf auch Metazeichen (*, ?, []) enthalten, die beim Vergleich ausgewertet werden. Hinter der Zeichenkette dürfen sich – durch senkrechte Striche getrennt – weitere Zeichenketten befinden. Dann wird mit allen vorhandenen Zeichenketten verglichen. Wenn der Vergleich ergibt, dass der Wert von Variable mit der oder den Zeichenketten übereinstimmt, werden die nach einer runden, rechten Klammer9 stehenden Kommandos ausgeführt. Die Kommandos müssen mit zwei Semikolons abgeschlossen werden. Danach wird das case-Kommando verlassen. Ergibt der Vergleich, dass der Wert von Variable nicht mit der oder den Zeichenketten übereinstimmt, wird nach der nächsten Zeichenkette (Zeichenkette2) gesucht und dort wieder verglichen. Wenn dieser Vergleich zutrifft, werden die hinter den Zeichenketten stehenden Kommandos (Kommandos2) ausgeführt. Wenn nicht, wird zur nächsten Zeichenkette gesprungen. Das caseKommando wird oft zur Auswertung von Parametern benutzt, beispielsweise in Startskripten wie /etc/init.d/skeleton. Das folgende Beispiel zeigt, wie dies zur Auswertung langer und kurzer Optionen geschieht: #!/bin/bash # Voreinstellungen festlegen verbose=0 force=0 version="0.99982p21" files="" for i; do case $i in -h | -\,-h* ) cat « EOF $0 - Skript mit Optionen -f | -\,-force : Aktionen ohne Rueckfrage durchfuehren. -v | -\,-verbose : Erklaeren, was getan wird. -h | -\,-help : Zeigt diese Hilfe an. -V | -\,-version : Version des Programms ausgeben. EOF exit ;; -v | -\,-verb* ) verbose=1 ;; -V | -\,-vers* ) echo $version; exit ;; -f | -\,-f* ) force=1 ;; ) if [ -r $i ] * then files=$files" "$i else echo $i" ist nicht lesbar!"; exit 1; fi ;; esac; done; 9
Der seltene Fall, dass eine Klammer ohne ihr Gegenstück auftritt.
2.7 Kommandointerpreter (Bash)
107
if [ $verbose -ne 0 ]; then echo "Bearbeite "$files; fi # Hier folgen die eigentlichen Aktionen. Quelle 2.14 : Shellskript optionen.sh als Beispiel für eine case-Anweisung
Das Beispiel stellt lediglich den Beginn eines Skriptes dar, die eigentlichen Aktionen fehlen. Wichtig ist die Methode, mit der hier die Kommandozeile auf Optionen untersucht wird. Nach der Deklaration von Variablen im oberen Teil wird eine for-Schleife über alle Positionsvariablen gestartet. Die Variable i enthält für jeden Durchlauf dieser Schleife einen Parameter, der dem Skript an der Kommandozeile übergeben wurde. Innerhalb der Schleife befindet sich lediglich die case-Anweisung. In dieser wird der Wert, der sich gerade in der Variablen i befindet, mit jeweils zwei Zeichenketten verglichen, die durch den senkrechten Strich voneinander getrennt sind. Die erste der beiden Zeichenketten repräsentiert die Option in Kurzschreibweise (mit einem Minuszeichen) und die zweite Zeichenkette die Option in langer Schreibweise (mit zwei Minuszeichen). Um die Benutzung des Skripts einfach zu gestalten, wird bei der langen Schreibweise nur mit den Zeichen am Anfang von Optionen verglichen, die notwendig sind, um eine Option von anderen zu unterscheiden. Hinter den Anfangszeichen befindet sich das Metazeichen Sternchen, das eine beliebige Anzahl beliebiger Zeichen bedeutet. Sobald eine Zeichenkette mit der Variablen i übereinstimmt, werden die Kommandos hinter der runden Klammer nach der entsprechenden Zeichenkette ausgeführt. Wenn beispielsweise die Option -h oder --help benutzt wurde, führt das dazu, dass der Hilfetext hinter dem cat-Kommando ausgegeben und das Programm verlassen wird. Eine ähnliche Wirkung hat die Option -V bzw. --version, nur dass hier das Kommando echo benutzt wird, um Text auszugeben. Die Optionen -f bzw. --force und -v bzw. --verbose führen dazu, dass den Variablen force bzw. verbose neue Werte zugewiesen werden. Hier folgt kein exit-Kommando, weshalb die for-Schleife erneut durchlaufen wird. Für die weitere Ausführung des Skripts ist wichtig, dass den beiden Variablen am Anfang bereits Werte zugewiesen wurden. Wenn diese Optionen nämlich nicht benutzt werden, werden den Variablen in der Schleife keine Werte zugewiesen, und die Auswertung der Variablen in späteren Teilen des Skripts führt zu Fehlern. Als letzte zu prüfende Zeichenkette befindet sich in der case-Anweisung das Metazeichen *. Dieses Zeichen trifft auf alle Zeichenketten zu. Deshalb werden die Anweisungen hinter dieser Zeichenkette immer ausgeführt, wenn nicht vorher der Vergleich mit einer anderen Zeichenkette erfolgreich gewesen ist. Die Anweisungen hinter dem Stern haben folgenden Sinn: Immer wenn sie ausgeführt werden, wird ein Parameter bearbeitet, der keine gültige Option darstellt. Das Skript muss nun entscheiden, ob es mit solchen Parametern überhaupt etwas anfangen kann. Angenommen, das Skript würde irgendwelche Aktionen mit Dateien ausführen, so könnte hier geprüft werden, ob es sich bei den hier bearbeiteten Parametern um Dateinamen
108
2 Linux/UNIX
handelt. Genau das wird hier getan. Mit einer test-Anweisung ([ -r $i ]) wird geprüft, ob der gerade bearbeitete Parameter dem Namen einer Datei entspricht, die lesbar ist. Falls das zutrifft, wird der Parameter an die in der Variablen files gespeicherte Zeichenkette angehängt. Ansonsten wird eine Fehlermeldung ausgegeben und das Skript mit dem Rückgabewert 1 beendet. Die Folge ist, dass alle Parameter, die keine Optionen, aber Namen von lesbaren Dateien sind, nach Beendigung des Skriptteils in der Variablen files gespeichert sind. Die Variable kann im weiteren Verlauf des Skripts steuern, welche Dateien bearbeitet werden. Die letzte Anweisung im Beispiel prüft, ob der Wert der Variablen verbose von 0 abweicht, was der Fall sein sollte, falls die Option -v bzw. --verbose benutzt wurde. Dann wird der Inhalt der Variablen files ausgegeben. Angenommen, das Skript wird unter dem Namen optionen.sh als ausführbare Datei im Arbeitsverzeichnis gespeichert, dann führt der Aufruf des Skripts in der Form: joe@debian:~$ ./optionen.sh -v * dazu, dass die Namen aller Dateien im Arbeitsverzeichnis ausgegeben werden, sofern sie für den aufrufenden Benutzer lesbar sind. Es wird zunächst durch den Parameter -v die Variable verbose auf 1 gesetzt. Der Stern wird von der Shell schon vor dem eigentlichen Aufruf des Skriptes durch die Namen der Dateien im Arbeitsverzeichnis ersetzt. Diese werden dann von dem Skript nacheinander als Parameter erkannt, die keine Optionen sind und daraufhin untersucht, ob sie Namen lesbarer Dateien sind. Wenn das so ist, werden sie – wie beschrieben – dem Wert der Variablen files angehängt, die am Ende des Skriptes aufgrund des Wertes der Variablen verbose ausgegeben wird. Funktionen Die Bash kennt Funktionen wie andere Programmiersprachen auch. Darunter werden Gruppen von Kommandos verstanden, denen ein Name zugeordnet wird, und die später mit diesem Namen aufgerufen werden. Die Benutzung von Funktionen bietet sich immer an, wenn eine Gruppe von Kommandos mehrmals ausgeführt werden soll. Mit Funktionen lassen sich längere Shellskripte übersichtlich gestalten. Die Syntax für Funktionen ist folgende: Name () { Kommando; [ Kommando; ... ] } Hierdurch wird eine Funktion mit dem Namen Name definiert. Die Funktion wird danach wie ein gewöhnliches Kommando benutzt. Sobald sie aufgerufen wird, werden die mit Kommando angegebenen Kommandos zwischen den geschweiften Klammern der Funktionsdefinition ausgeführt. Bei der Definition von Funktionen gibt es zwei Dinge zu beachten: • •
Die Funktion muss vor ihrem ersten Aufruf definiert sein, naheliegend. Die Positionsvariablen 1, 2 usw. werden beim Aufruf einer Funktion durch die Parameter ersetzt, mit denen die Funktion aufgerufen wurde. Nach Beendigen der Funktion sind wieder die alten Werte gültig.
2.7 Kommandointerpreter (Bash)
109
Funktionen werden an jeder Stelle durch das Kommando return beendet. Wir haben bereits ein Beispiel für eine Funktion in Quelle 2.5 auf Seite 96 kennen gelernt. Hier noch ein Beispiel: #!/bin/bash hallo () { if [ -z $1 ] then echo "hallo "$USER else echo "hallo "$1 fi; } hallo hallo Joe Debian Quelle 2.15 : Shellskript hallo.sh als Beispiel für eine Shellfunktion
In dem Skript wird zunächst eine Funktion mit dem Namen hallo definiert. In der Funktion wird geprüft, ob die Positionsvariable 1 leer ist, und falls ja, die Zeichenkette hallo und der in der Variablen USER gespeicherte Wert ausgegeben. Wenn die Positionsvariable nicht leer ist, wird stattdessen ebenfalls die Zeichenkette hallo, aber danach die in der Positionsvariablen 1 gespeicherte Zeichenkette ausgegeben. Im unteren Teil des Skripts wird die Funktion zweimal aufgerufen. Einmal ohne Parameter, was zur Ausgabe von hallo und dem Benutzernamen des Aufrufers führt, und einmal mit dem Parameter Joe Debian, was zur Ausgabe der Zeichenkette hallo Joe Debian führt. Als reizvolleres Beispiel sehen wir uns ein Start- und Stopskript an. In Abschnitt 11.8.4 Start- und Stopskripte auf Seite 505 ist beschrieben, wie vom Programm init während des Systemstarts und beim Wechseln des Runlevels bestimmte Skripte ausgeführt werden. Jedes dieser Skripte ist für das Starten oder Anhalten eines Dienstes oder für das Ein- bzw. Ausschalten bestimmter Systemeigenschaften zuständig. Diese Skripte akzeptieren mindestens zwei Argumente, nämlich start und stop. Beim Starten eines Dienstes wird das Skript von init mit dem Argument start aufgerufen und beim Anhalten mit dem Argument stop. Möchte man weitere Aktionen beim Systemstart oder beim Wechseln von Runleveln durchführen oder eigene Dienste starten, dann empfiehlt es sich, eigene Skripte zu diesem Zweck zu schreiben, die als ausführbare Dateien im Verzeichnis /etc/init.d abgelegt werden. Danach muss ein symbolischer Link in das /etc/rc*.d-Verzeichnis des Runlevels, in dem der Dienst zur Verfügung stehen soll, gesetzt werden, und schon wird der Dienst automatisch bei jedem Wechsel in den entsprechenden Runlevel gestartet. Das folgende Skript definiert den Dienst Soundunterstützung am Beispiel einer AWE32-Soundkarte. Um die Unterstützung dieser Karte zur Verfügung zu stellen, sollen eine Reihe von Kern-Modulen mit konfigurierbaren Parametern geladen werden. Darüber hinaus soll ein spezielles Programm aufgerufen werden, mit dem Soundfonts auf der Karte eingerichtet werden. Das hierbei verwendete Programm und die Soundfont-Datei sollen ebenfalls konfigurierbar sein. Deshalb wurde das fol-
110
2 Linux/UNIX
gende Skript zweigeteilt: zum einen in das eigentliche Startskript, das von init aufgerufen wird, und zum anderen in eine Konfigurationsdatei, in der angegeben wird, welche Module geladen oder entfernt werden sollen, welche Parameter dabei zu verwenden sind und mit welchem Programm welche Soundfont-Datei geladen werden soll. Die Datei mit dem Startskript bekommt den Namen /etc/init.d/sound und die Konfigurationsdatei den Namen /etc/sound.config. Ein zusätzliches Problem ergibt sich dadurch, dass die zu ladenden Module voneinander abhängen. Sie müssen in einer bestimmten Reihenfolge geladen und beim Anhalten des Dienstes in umgekehrter Reihenfolge entladen werden. Das Skript soll sich um diesen Sachverhalt automatisch kümmern10 . Im Kopfteil des Startskripts findet sich neben der Kommentarzeile die Initialisierung für zwei Variable: do_sfx und rmodules. In der Variablen do_sfx wird festgehalten, ob Soundfonts auf die Karte zu laden sind, und in der Variablen rmodules werden die Namen der Module in umgekehrter Reihenfolge gespeichert. Die Reihenfolge wird zum geordneten Entladen der Module benötigt: #!/bin/bash # Starten des Sound-Subsystems do sfx=0 rmodules="" if [ else test test
-r /etc/sound.config ]; then . /etc/sound.config; exit 0; fi; -z "$modules" && exit 0: -x "$sfxload" && test -r "$sfxdatei" && do sfx=1;
case "$1" in start) echo -n "Lade Sound-Treiber... " for i in $modules do opts=‘eval echo ’$’$i‘ modprobe $i $opts done; [ $do sfx -eq 1 ] && $sfxload $sfxdatei echo "fertig." ;; stop) echo -n "Entferne Sound-Treiber... " for i in $modules; do rmodules=$i" "$rmodules; done; for i in $rmodules; do rmmod $i; done; echo "fertig." ;; restart) $0 stop $0 start ;; force-reload) 10
Alternativ zu dem hier gezeigten Verfahren lässt sich das Laden von Modulen auch mit dem Programm modprobe automatisieren.
2.7 Kommandointerpreter (Bash)
111
$0 restart ;; *) echo "Verwendung: $0 {start|stop|restart|force-reload}" exit 1 esac exit 0 Quelle 2.16 : Shellskript sound zum Starten und Stoppen eines Sound-Dienstes
Darunter folgen drei Anweisungen, mit denen geprüft wird, ob genügend Informationen vorliegen, um alle Aktionen auszuführen. Die erste dieser Anweisungen testet, ob die Konfigurationsdatei /etc/sound.config vorhanden ist und gelesen werden kann. Falls ja, werden die Anweisungen in der Konfigurationsdatei ausgeführt (der Punkt ist eine Abkürzung für das Kommando source). Wenn die Konfigurationsdatei nicht vorhanden oder nicht lesbar ist, wird das Skript sofort und ohne einen von 0 abweichenden Rückgabewert verlassen. Der Grund hierfür ist, dass das Startskript auf Systemen, auf denen keine Soundkarte vorhanden ist oder diese noch nicht konfiguriert wurde, nicht zu Fehlern beim Start führen soll. Die Konfigurationsdatei /etc/sound.config könnte folgendermaßen aussehen: # /etc/sound.config # Konfigurationsdatei fur /etc/init.d/sound # Namen der zu ladenden Module in der Reihenfolge, in # der sie geladen werden sollen. modules="soundcore soundlow sound uart401 sb opl3 awe wave" # Optionen zum Laden der Module # Syntax: Modulname="Optionen" sb="irq=5 dma=0 dma16=5 mpu io=0x330 io=0x220" opl3="io=0x388" # Pfad und Name des Programms sfxload sfxload="/usr/bin/sfxload" # Pfad und Name der zu ladenden Sounddatei sfxdatei="/usr/lib/awe/sfbank/synthgm.sbk" Quelle 2.17 : Konfigurationsdatei für einen Sound-Dienst
Die Datei enthält gewöhnliche Bash-Kommandos, unterscheidet sich jedoch von einem Shellskript dadurch, dass sie nicht ausführbar zu sein braucht, weswegen ihr auch die sonst übliche Kommentarzeile mit Angabe des Interpreters (#!/bin/bash) fehlt. Dies liegt daran, dass sie vom Startskript nicht als ein eigener Prozess aufgerufen wird, sondern lediglich die in ihr angegebenen Kommandos
112
2 Linux/UNIX
gelesen und ausgeführt werden. Obwohl hier auch andere Kommandos möglich wären, befinden sich in dieser Datei nur Variablendefinitionen mit Wertzuweisungen. Für den Benutzer, der nur diese Datei zu ändern braucht, reicht es aus, hinter die Gleichheitszeichen Werte entsprechend seiner Konfiguration zu schreiben. Die erste Nicht-Kommentar-Zeile definiert die Variable modules, in der die Namen der zu ladenden Module gespeichert werden. Darunter befinden sich Optionsdefinitionen für einige Module, die in Variablen gespeichert werden, welche die gleichen Namen tragen wie die Module, für die sie gelten. Schließlich folgen die Definitionen von zwei Variablen, die den Namen des Programms zur Einrichtung von Soundfonts und den Namen der einzurichtenden Soundfont-Datei beinhalten. Im Startskript wird nach der Bearbeitung der Konfigurationsdatei geprüft, ob die Variable modules leer ist. Falls ja, wird das Skript verlassen. Dann wird geprüft, ob eine ausführbare Datei zum Laden von Soundfonts angegeben wurde und ob die Soundfont-Datei lesbar ist. Nach der Initialisierungsphase wird in einer case-Anweisung der erste dem Skript übergebene Parameter (Variable 1) ausgewertet. Abhängig vom Wert des Parameters werden durch die case-Anweisung fünf verschiedene Blöcke des Skripts ausgeführt: • • •
•
•
start Anweisungen zum Starten des Dienstes. stop Anweisungen zum Anhalten des Dienstes. restart Die Option wird von vielen Startskripten aus Bequemlichkeit zur Verfügung gestellt. Der Dienst wird angehalten und sofort erneut gestartet. Dies wird erreicht, indem sich das Skript selbst einmal mit dem Parameter stop und dann mit dem Parameter start aufruft. Dazu wird nicht der Name des Skripts, sondern die Variable 0 benutzt, in der sich der Name des Skriptes befindet. Dies bietet den Vorteil, dass die Funktion auch dann noch funktioniert, wenn das Skript umbenannt wurde. force-reload Einige Programme, die als Systemdienste benutzt werden, können ihre Konfigurationsdaten neu lesen, ohne angehalten und neu gestartet werden zu müssen. Deswegen implementieren viele Startskripte die Option force-reload, die solchen Programmen ein bestimmtes Signal schickt, woraufhin die Konfigurationsdaten gelesen werden. In unserem Fall ist dies nicht möglich, weswegen die Option hier dazu führt, dass das Skript nochmals mit dem Parameter restart aufgerufen wird. * Der Stern zum Schluss fängt den Fall ab, dass mit dem ersten Parameter eine nicht unterstützte Option ausgewählt wurde. In diesem Fall wird eine Hilfe mit den gültigen Optionen ausgegeben.
Der start-Teil beinhaltet im Wesentlichen eine Schleife über die in der Variablen modules befindlichen Werte, das heißt über die Namen der Module. In der ersten Anweisung der Schleife wird ausgewertet, mit welchen Optionen das jeweilige Modul zu laden ist. Die Konvention ist, dass die Optionen für jedes Modul in einer Variablen mit dem Namen des Moduls gespeichert sind. Daher wird zunächst vor den Namen des Moduls (in i) das Dollarzeichen gesetzt, um den Namen als Variable zu deklarieren. Weil diese neu entstandene Variablenbezeichnung nicht automatisch
2.7 Kommandointerpreter (Bash)
113
durch ihren Wert ersetzt wird, wird sie dem Kommando eval übergeben, der eine weitere Ersetzung durchführt. Da eval die Zeichenkette auflöst und als Kommando interpretiert, ist der Anweisung das Kommando echo vorangestellt, welches zusammen mit eval die Optionen für das zu ladende Modul ausgibt. Diese Ausgabe wird mittels Kommandosubstitution der Variablen opts zugewiesen. Dann wird das Modul mit diesen Optionen über das Programm modprobe geladen. Wenn die Schleife beendet ist und alle Module geladen sind, wird die SoundfontDatei eingerichtet (Programmname in sfxload11 , Datei in sfxdatei), falls die Bedingungen hierzu erfüllt sind. In diesem Fall wäre die Variable do_sfx im Initialisierungsteil auf den Wert 1 gesetzt worden. Der stop-Teil beinhaltet zwei Schleifen. In der ersten werden die Namen der Module aus der Variablen modules in umgekehrter Reihenfolge in der Variablen rmodules gespeichert, indem der jeweils aktuelle Modulnamen vor die dort bereits vorhandenen gesetzt wird. In der zweiten Schleife werden diese Module mit dem Kommando rmmod entfernt. In den meisten Fällen ist ein Startskript zum Laden von Modulen nicht notwendig. Vielmehr können Module durch das Kernprogramm kmod automatisch entsprechend der Konfiguration in /etc/modules.conf geladen werden. Gelegentlich ist es wünschenswert, bestimmte Module nur explizit zur Verfügung zu stellen. Dadurch wird global verhindert, dass Programme die Soundkarte benutzen, wenn dies nicht gewollt ist. Vorstehendes Beispiel dient nur der Veranschaulichung des Aufbaus von Shellskripten und darf keineswegs als Anleitung für die Einrichtung von Sound auf einem Rechner verstanden werden. 2.7.8 Andere Skriptsprachen Unter Debian GNU/Linux stehen eine Reihe weiterer interpretierter Sprachen (Skriptsprachen) zur Verfügung, die für manche Anwendungen besser geeignet sind als die Bash. Die bekanntesten dieser Sprachen sind Perl (http://perl. org/), Python (http://www.python.org/) und Tcl/TK (http://www. tcl.tk/). Weitere, weniger bekannte finden sich in der Abteilung Interpreters der Debian-Paketliste auf http://packages.debian.org/. Als Beispiel für ein Perlskript diene Quelle 2.18 , das nach Perl umgeschriebene Shellskript 2.3 von Seite 90 zur Berechnung von Primzahlen. Die dortige Erklärung gilt auch hier. #!/usr/bin/perl # perl-Script zur Berechnung von Primzahlen $ende = 10000; $z = 5; $i = 1; @p = (2, 3); $n = 2; 11
# # # # #
groesste Zahl aktuelle Zahl Index von p Array der Primzahlen Anzahl der Primzahlen
Das Programm sfxload ist Bestandteil des Paketes awe-drv. Es wird zum Laden von Soundfonts auf Soundkarten der Soundblaster AWE-Familie benutzt.
114
2 Linux/UNIX
while ($z <= $ende) { if ($z % @p[$i] == 0) { # z teilbar $z = $z + 2; $i = 1; } else { # z nicht teilbar if (@p[$i] * @p[$i] <= $z) { $i++; } else { @p[$n] = $z; $n++; $z = $z + 2; $i = 1; } } } # Ausgabe des Arrays $i = 0; while ($i < $n) { print(@p[$i++], "\n"); } print("Anzahl: ", $n, "\n"); Quelle 2.18 : Perlskript prim.pl zur Berechnung von Primzahlen
Perl hat Stärken in der Verarbeitung von Zeichenketten. Python ist eine anpassungsfähige, zunächst einfache Skriptsprache, die gut mit anderen Programmiersprachen zusammenarbeitet und gern auf Webservern eingesetzt wird. Die Sprache Tcl mit dem Toolkit Tk ist anfangs einfach und bringt Konstrukte für das Arbeiten mit Dateien, Grafiken und im Netz mit. Weitere einführende Informationen finden sich in der Wikipedia. Darüber hinaus können andere Shells eingesetzt werden. Die bekanntesten Alternativen zur Bash sind die Korn-Shell und die Tenex C Shell, siehe die Abteilung Shells der Debian-Paketliste. Von den interpretierten Sprachen sind solche zu trennen, deren Quellen zunächst mit einem Compiler in Prozessoranweisungen übersetzt werden und dann ohne weitere Übersetzung ausgeführt werden können. Diese Sprachen bieten den Vorteil, dass in ihnen verfasste Programme wesentlich schneller als Skripte ausgeführt werden. Die klassische Programmiersprache unter UNIX und Linux ist C mit seiner mächtigen Erweiterung C++. Linux selbst und die meisten unter Debian GNU/Linux eingesetzten Programme sind in C/C++ geschrieben. Deswegen ist es nicht verwunderlich, dass diese Sprache einen reibungslosen Zugang zu den Funktionen des Betriebssystems bietet und unter Linux gut unterstützt wird.
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
115
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers 2.8.1 Grundbegriffe Dateien (E: file, F: fichier) enthalten Daten, die geschrieben oder gelesen werden. Das Konzept ist unter Linux/UNIX sehr allgemein zu verstehen. Auch ein Bildschirm ist eine Datei oder ein Teil davon. Unter einem Dateinamen werden zusammengehörige Daten zusammengefasst, beispielsweise ein Brief, ein Programm, eine Webseite, ein Abschnitt eines Buches, eine Zeichnung, ein Foto, ein Musikstück. Solche gewöhnlichen oder regulären Dateien werden in Verzeichnissen (Ordner, Katalog, E: directory, catalog, folder, F: répertoire) zusammengefasst, um die Übersicht zu behalten. Unter Linux/UNIX dürfen Datei- oder Verzeichnisnamen alle Zeichen aller Zeichensätze enthalten außer dem unsichtbaren Nullbyte (ASCII-Zeichen Nr. 0, 00000000) und dem Schrägstrich (ASCII-Zeichen Nr. 47, E: slash, F: barre oblique), weil dieser im Pfad Verzeichnisnamen voneinander trennt. Es empfiehlt sich jedoch, auf Sonder- und Steuerzeichen (Umlaute, Escape, Control) zu verzichten; man handelt sich nur unnötige Überraschungen damit ein. Auch aus mehreren, durch Zwischenraum getrennten Wörtern bestehende Dateinamen sind erlaubt, erfordern aber im Umgang zusätzlichen Aufwand (Anführungszeichen), weil der Zwischenraum von der Shell als Trennzeichen verstanden wird. Große und kleine Buchstaben sind wie immer in der Linux/UNIX-Welt verschiedene Zeichen. Heute dürfen Datei- und Verzeichnisnamen bis zu 255 Zeichen lang sein, früher waren es 14. Bei entsprechender Konfiguration sind sogar Dateinamen mit einer Länge von 1023 Zeichen möglich, aber da fragt man sich, wozu die gut sein sollen. Auch Verzeichnisse sind Dateien. Es handelt sich dabei um eine spezielle Form, nämlich um zweispaltige Tabellen (genauer: verkettete Listen oder Bäume) der Dateien und Unterverzeichnisse, die sich in den Verzeichnissen befinden, samt einer eindeutigen Nummer – der Inode-Nummer – über die man zu Informationen über die jeweilige Datei gelangt. Die Zuordnung von Dateiname und Inode-Nummer erfolgt ausschließlich in Verzeichnissen. Diese werden vom System verwaltet und können vom Benutzer nur gelesen werden, nicht unmittelbar verändert, da dies den Aufbau des Dateisystems und den Zugang zu den Daten gefährden würde. Zu jeder Datei einschließlich der Verzeichnisse gehört ein Inode12 . Der Inode (INode, index node, Informationsknoten, Indexeintrag, Dateikopf) ist selbst eine Art von kleiner Datei, die alle Informationen über eine Datei (Metadaten) mit Ausnahme des Namens beherbergt. Dazu gehören: • • • • •
Besitzer (E: owner, F: propriétaire) der Datei, Gruppe (E: group, F: groupe) der Datei, Zugriffsrechte, siehe Abschnitt 2.8.3 Dateiattribute, Zugriffsrechte, Zugriffskontrolllisten auf Seite 120, Art der Datei (reguläre Datei, Verzeichnis, Symlink . . . ), Größe in Bytes, 12
Der, die, das Inode? E: node bedeutet Knoten, die Wörter sind auch verwandt, also gebrauchen wir der Inode. Bei Firewall und Socket taucht die Frage ebenfalls auf.
116
• •
•
2 Linux/UNIX
Referenzzähler (link counter), das heißt Anzahl der Namen der Datei (harte Links), drei Zeitstempel: – Zeit des jüngsten öffnenden Zugriffs (atime, access time), – Zeit des jüngsten schreibenden Zugriffs (mtime, modification time), – Zeit der jüngsten Änderung des Dateistatus (in etwa: Änderung des Inodes, ctime, status change time), Verweise (Zeiger, Adressen) auf die Datenblöcke der Datei.
Der Inode ist wichtiger als die Datenblöcke. Es gibt Dateien ohne Datenblöcke – beispielsweise die Gerätedateien oder kurze Symlinks – aber keine Datei ohne Inode13 . Der Aufruf: joe@debian:~$ stat dateiname zeigt die Informationen zu einer Datei an, einen Teil davon auch das Kommando ls mit entsprechenden Optionen, beispielsweise: joe@debian:~$ ls -li dateiname Die Manualseite zum Systemaufruf stat (Sektion 2) enthält ebenfalls Einzelheiten zum Inode. Die Inodes werden in einer eigenen Liste im Dateisystem gespeichert. Nach der Anmeldung landet ein Benutzer in einem Verzeichnis, das ihm gehört, in dem er alle Rechte besitzt (möglichst aber auch nur er, von Root abgesehen). Das Verzeichnis wird Home-Verzeichnis (Heimatverzeichnis, Hauptkatalog, E: home directory, F: répertoire personnel, répertoire principal) genannt. Dort Ordnung zu halten, bleibt jedem Benutzer selbst überlassen. Im Interesse der Systemsicherheit kann der Verwalter bestimmte Vorgaben machen (umask, Quoten). Um Dateien zu bearbeiten, werden Programme verwendet, die mit den Informationen, die in einer Datei zu finden sind, etwas anfangen können. So bearbeitet man Textdateien mit einem Texteditor oder einem Textverarbeitungsprogramm, Bilddateien mit Grafikprogrammen, Sounddateien mit Soundwerkzeugen usw. Unser zweites Debian-Buch widmet sich eingehend solchen Werkzeugen, da hier der Platz nicht ausreicht. Für Linux/UNIX sind alle gewöhnlichen Dateien Folgen von Bytes; es unterscheidet nicht zwischen Text und anderen Inhalten. Das ist nicht überall so. Auf den nachstehenden Seiten werden Dateien und Verzeichnisse behandelt, wie sie sich dem Benutzer darbieten. Die technischen Einzelheiten von Dateisystemen wie ext3 kommen im Abschnitt 13 Dateisysteme ab Seite 563 zur Sprache. 2.8.2 Verzeichnis- und Dateibaum Viele Anwender, die zum ersten Mal mit einem Linux/UNIX-Betriebssystem arbeiten, sind zunächst von der Fülle der Dateien und Verzeichnisse auf dem System verwirrt. Es sind Zehntausende. Während sich die Dateien einfacher Betriebssysteme in 13
Bei Pipes (FIFOs) und Sockets ist die Aussage zu relativieren, aber das sind auch keine richtigen Dateien.
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
117
nur wenigen Verzeichnissen befinden, wird bei der Einrichtung eines Linux/UNIXSystems eine Vielzahl von Verzeichnissen angelegt, die alle mehr oder weniger Teile des Betriebssystems oder der Distribution beinhalten. Es steckt aber Logik hinter der Vielfalt. Zwei Gesichtspunkte bei der Einteilung sind: • •
statische, sich selten ändernde Daten versus sich laufend ändernde, systemübergreifende (shareable) Daten versus systemspezifische.
Daraus folgen vier Kombinationen. Dateien, die Dokumentation wie das Manual enthalten, sind beispielsweise statisch und systemübergreifend. Ein weiterer Gesichtspunkt ist, dass bestimmte Daten beim Systemstart gebraucht werden, andere erst später. Wer einfach ein fertig konfiguriertes System benutzen möchte, braucht nur zu wissen, dass sich seine eigenen Dateien in seinem Home-Verzeichnis befinden. Dieses Verzeichnis ist ein Unterverzeichnis des Verzeichnisses /home und trägt üblicherweise den Namen des Besitzers. Die Verzeichnisse und Dateien bilden eine baumartige Hierarchie mit der Wurzel oben. Das Wurzelverzeichnis (E: root directory, F: répertoire racine) wird nur mit einem Schrägstrich bezeichnet, vom Kern während des Systemstarts eingebunden und während der ganzen Laufzeit beibehalten. Das Wurzelverzeichnis muss selbst – ohne Einhängen weiterer Partitionen oder Verzeichnisse – alles enthalten, was zum Systemstart und für etwaige Reparaturversuche benötigt wird. Mehr soll in das Wurzelverzeichnis auch nicht hineingepackt werden, damit es klein und einfach bleibt. Dazu kommen im Betrieb weitere, automatisch oder per Kommando eingehängte Partitionen oder Verzeichnisse. Im Mehrbenutzerbetrieb enthält das Wurzelverzeichnis nach dem Einhängen aller erforderlichen Partitionen folgende Verzeichnisse: •
•
•
•
•
/bin enthält klassische Linux/UNIX-Kommandos, die schon beim Start, bei Reparaturen und beim Zurückspielen von Sicherungskopien (Backups) gebraucht werden wie mount, bash oder ls. Es muss sich auf derselben Partition befinden wie das Wurzelverzeichnis. Die Kommandos sind großenteils übersetzte (compilierte) Programme, zum Teil aber auch Shellskripte. Sie werden von allen Benutzern und vielen Shellskripten verwendet. Keine Unterverzeichnisse. /boot mit statischen Dateien, die beim Start gebraucht werden, bevor das Wurzelverzeichnis eingebunden ist. Hier liegt beispielsweise der Linux-Kern, der von einem Bootloader wie LILO oder GRUB in den Arbeitsspeicher geladen wird. /dev Gerätedateien und ein einziges Kommando (MAKEDEV), nichts anderes. Muss auf derselben Partition liegen wie das Wurzelverzeichnis. Weiteres siehe Abschnitt 2.8.4 Gerätedateien auf Seite 127. /etc für Dateien zur systemweiten Konfiguration, mit vielen Unterverzeichnissen, wertvoll, da mit dem Schweiß des Verwalters getränkt. Muss auf derselben Partition liegen wie das Wurzelverzeichnis. Früher enthielt /etc auch Kommandos zur Systemverwaltung, die heute in /sbin oder /usr/sbin liegen. /home mit den Home-Verzeichnissen der Benutzer, anderswo auch /homes oder /users genannt. Üblicherweise auf einer eigenen Partition untergebracht, die während des Systemstarts in das Wurzelverzeichnis eingehängt wird. Kann über das Netz per NFS oder Samba von einem zentralen Dateiserver zur Verfü-
118
• •
• • •
•
•
•
•
•
2 Linux/UNIX
gung gestellt werden. Falls man Tausende von Benutzern hat, in Unterverzeichnisse für Benutzergruppen unterteilt. /lib Bibliotheken und Kernmodule, die beim Start gebraucht werden, daher auf derselben Partition wie das Wurzelverzeichnis, /lost+found Fundbüro für Dateien, die bei einem File System Check keinem Verzeichnis zugeordnet werden können, normalerweise leer. Jede Partition mit einem ext2/ext3-Dateisystem betreibt ihr eigenes Fundbüro. /media enthält Unterverzeichnisse (Einhängepunkte) für entfernbare Medien wie Disketten, CD/DVDs, USB-Stöpsel oder Kameras. /mnt vorgefertigter, aber unverbindlicher Einhängepunkt für beliebige Dateisysteme. /opt beherbergt optionale Programmpakete wie den Adobe Reader oder Google Earth, sofern diese nicht im Verzeichnis /usr/local untergebracht werden. Auch besondere Compiler passen gut dorthin. Im File Hierarchy Standard vorgesehen, unter Linux eher selten anzutreffen. Gern über das Netz zur Verfügung gestellt, um die Verwaltung der optionalen Programmpakete zu vereinfachen. Nicht für Kleinvieh (lokale Shellskripte) gedacht. /proc virtuelles Dateisystem zum Zugriff auf Informationen aus dem Kern. Enthält keine Dateien mit Daten. Näheres siehe Abschnitt 13.8 Virtuelle Dateisysteme auf Seite 587. Der Nachfolger heißt /sysfs. /root Home-Verzeichnis von Root, auf derselben Partition wie das Wurzelverzeichnis, damit es dem Verwalter auch im Single-User-Modus zugänglich ist. Nicht gedacht für die Arbeit des Verwalters als gewöhnlicher Benutzer; hierfür sollte der Verwalter ein Konto als gewöhnlicher Benutzer mit Home-Verzeichnis unter /home verwenden. /sbin dem Verwalter vorbehaltene Werkzeuge, die beim Systemstart oder bei Reparaturen gebraucht werden, wie fsck oder ifconfig. Muss daher auf derselben Partition liegen wie das Wurzelverzeichnis. /tmp temporäre Dateien, meist von Anwendungsprogrammen angelegt, üblicherweise auf eigener Partititon. Wird beim Systemstart geleert, unter Umständen auch häufiger. /usr umfangreiches Verzeichnis mit der Mehrzahl der Programme, Bibliotheken und Dokumentationen, die im laufenden Betrieb von allen Benutzern gebraucht werden. Bevorzugt auf eigener Partition, rund ein Dutzend Unterverzeichnisse. Früher auch mit Unterverzeichnissen, die heute unter /var liegen. – /usr/bin allgemeine Kommandos, soweit nicht in /bin, – /usr/doc Dokumentation, heute nur noch mit Symlinks auf /usr/ share/doc oder fehlend, – /usr/games Spiele und pädagogische Software, – /usr/include Include-Dateien (Header-Dateien) für C/C++-Programme, teilweise mit interessanten Informationen, – /usr/lib Bibliotheken, auch Programme, die nicht direkt von Benutzern oder Skripten aufgerufen werden, – /usr/local Heimstatt für lokale Besonderheiten, eigene Züchtungen und dergleichen, Unterverzeichnisse ähnlich wie /usr, ziemlich wertvoll,
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
•
119
– /usr/sbin Werkzeuge zur Verwaltung, die nicht schon beim Systemstart oder für Reparaturen benötigt werden, – /usr/share systemunabhängige Daten wie Dokumentation oder Wörterbücher, oft von einem Server in das lokale Netz exportiert, – /usr/src Quellcode (source code), auf vielen Arbeitsplätzen leer, – /usr/X11R6 X Window System Version 11 Release 6, wegen seiner Bedeutung und seines Umfangs in einem eigenen Verzeichnis untergebracht. Entsprechend künftig X11R7. /var mit Dateien, deren Größe starkt schwankt, typischerweise Protokolldateien (log-Dateien), Warteschlangen (Email, Drucker), Webspace und dergleichen, Meist auf eigener Partition, rund ein Dutzend Unterverzeichnisse je nach Ausbau des Systems: – /var/account Buchaltungsdaten, sofern eingerichtet, – /var/cache Zwischenspeicher einiger Anwendungen, – /var/games Spieldaten, – /var/lib veränderliche Zustandsdaten, – /var/lock Sperrdateien (lockfile) einiger Anwendungen, – /var/log Protokolldateien, mehrere Unterverzeichnisse, wichtig für die Fehlersuche, – /var/mail Briefkästen der Benutzer, – /var/run veränderliche Daten laufender Prozesse, – /var/spool Warteschlangen einiger Anwendungen, – /var/tmp temporäre Dateien, die einen Systemstart überdauern sollen, – /var/www Webspace, sofern ein Webserver eingerichtet ist, – /var/yp NIS-Daten, sofern ein NIS-Server eingerichtet ist.
Das Wurzelverzeichnis enthält wenige Dateien, meist Symlinks auf Boot-Dateien in den Verzeichnissen. Der Verwalter kann nach reiflicher Überlegung weitere Verzeichnisse unter der Wurzel anlegen, vor allem Einhängepunkte. Jedes Verzeichnis gehört in ein übergeordnetes Verzeichnis, nur das Wurzelverzeichnis ist sich selbst über- oder untergeordnet. Die Dateien bilden die Blätter des Baums, unter ihnen folgt nichts mehr. Im Lauf der Zeit sollte man eine Vorstellung davon entwickeln, was wohin gehört. Die Namen der wichtigsten Verzeichnisse sowie ihre Lage stimmen zwischen den meisten Linux-Distributionen und UNIX-Systemen überein. Sowohl die Namen als auch die Aufgaben dieser Verzeichnisse sind historisch gewachsen. Das Verzeichnis /etc (etc = et cetera, Sonstiges) würde man heute anders nennen, aber so heißt es nun einmal seit fast fünfzig Jahren. Trotz der groben Übereinstimmung zwischen allen Linux- und UNIX-Systemen gibt es im Detail Unterschiede. Hierunter leiden Verwalter, die mit Systemen verschiedener Hersteller arbeiten. Aber auch die Erstellung von Software und von Installationsskripten wird durch Unterschiede im Verzeichnisbaum erschwert. Aus diesem Grund gibt es Bemühungen zur Standardisierung, deren Ergebnis der File Hierarchy Standard (FHS) ist, zur Zeit in der Version 2.1. Der Standard ist in dem Paket debian-policy enthalten und befindet sich nach dessen Einrichtung im Verzeichnis /usr/share/doc/debian-policy/fhs.
120
2 Linux/UNIX
Englischkenntnisse vorausgesetzt ist das Dokument leicht zu lesen. Der FHS in der neuesten Fassung ist von http://www.pathname.com/ abrufbar. Bei The Linux Documentation Project gibt es einen Führer Linux Filesystem Hierarchy (2004) von B INH N GUYEN. Die Verzeichnisstruktur ab Debian GNU/Linux 2.2 richtet sich nach dem FHS. Es gibt Bestrebungen, auch in die Home-Verzeichnisse, deren Gestaltung ihren Besitzern völlig frei gestellt ist, eine verbindliche Struktur hineinzubringen, siehe http://standards.freedesktop.org/. 2.8.3 Dateiattribute, Zugriffsrechte, Zugriffskontrolllisten Anzeigen von Dateiattributen Wie wir eben gesehen haben, gehören zu einer Datei Informationen über sie, auch Metadaten oder Attribute genannt, die in einem Inode abgelegt sind. Wird das Kommando ls mit der Option -l (long) aufgerufen, erfolgt die Ausgabe im langen Format. Geben Sie in einem Unterverzeichnis, sagen wir texte, Ihres HomeVerzeichnisses folgendes Kommando ein: joe@debian:~/texte$ ls -l Sie erhalten dann eine Ausgabe folgender Art: insgesamt 38 -rw-r--r-- 1 -rw-r--r-- 1
joe joe
joe joe
18007 18007
Jun 24 15:21 Jun 24 15:21
GPL-Beer GPL-Komm
In der oberen Zeile wird der Platzbedarf aller angezeigten Dateien in Kilobyte dargestellt. Darunter finden sich ausführliche Angaben zu den beiden Dateien in dem Verzeichnis, ein Ausschnitt aus dem Inode zuzüglich des Dateinamens: • • • • • • • • • •
Art der Datei, beispielsweise gewöhnliche Datei (-), Verzeichnis (d) oder symbolischer Link (l), Rechte des Besitzers an der Datei (read - write - execute), Rechte der Gruppe an der Datei, Rechte anderer Benutzer an der Datei, Anzahl der Verzeichniseinträge (hard link) der Datei, Besitzer der Datei, Gruppe der Datei, Größe der Datei in Bytes, Datum (Zeitstempel) der jüngsten Änderung der Datei (mtime), Name der Datei.
Bei symbolischen Links wird neben dem Namen des Links zusätzlich der Name der Datei angezeigt, auf die der Link verweist. Die Bedeutung der Felder wird auch in Abbildung 2.8 dargestellt. Neben gewöhnlichen Dateien, Verzeichnissen und Symlinks gibt es noch andere Dateitypen, die in Abschnitt 2.8.7 Besondere Dateiarten auf Seite 133 besprochen werden. Aus der oben genannten Ausgabe von ls -l lässt sich Folgendes ablesen:
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
121
Abb. 2.7: 1 Byte aus 8 Bits, einmal nicht durch 0 und 1 dargestellt, oder doch?
• • • • •
•
• •
Bei den beiden angezeigten Dateien handelt es sich um gewöhnliche (reguläre) Dateien, denn das erste Zeichen des ersten Feldes ist ein Bindestrich. Beide Dateien gehören dem Benutzer joe (drittes Feld, Besitzer) und sind der Gruppe joe zugeordnet (viertes Feld). Beide Dateien haben eine Größe von 18.007 Byte (fünftes Feld). Sie wurden zuletzt am 24. Juni des laufenden Jahres um 15:21 Uhr geändert (mtime, sechstes Feld). Der Besitzer joe darf die Dateien lesen (erstes Feld, zweites Zeichen ist r wie read) und inhaltlich ändern (erstes Feld, drittes Zeichen ist w wie write). Er darf die Datei nicht ausführen (erstes Feld, viertes Zeichen ist nicht x wie execute, sondern ein Strich). Mitglieder der Gruppe joe dürfen die Dateien lesen (erstes Feld, fünftes Zeichen), aber weder ändern noch ausführen (erstes Feld, sechstes und siebentes Zeichen). Ebenso dürfen alle anderen Benutzer die Dateien nur lesen (erstes Feld, achtes Zeichen), aber nicht ändern oder ausführen (erstes Feld, letzte beide Zeichen). Bleibt noch das zweite Feld, der Linkzähler. Er gibt an, wieviele Namen (harte Links) die Datei hat, im Minimum einen.
Mit anderen Optionen zeigt ls teilweise andere Attribute an, beispielsweise andere Zeitstempel. Das Kommando tree zeigt durch Einrückungen und ASCII-Grafik die Baumstruktur eines Verzeichnisses rekursiv an und versteht ähnliche Optionen wie ls. Auf Seite 665 steht ein Beispiel. Daten werden in Rechnern durch Bits dargestellt. Ein Bit ist das Datenatom. Es nimmt einen von zwei möglichen Werten an. Ob diese durch 0 und 1 oder durch andere Symbole dargestellt werden, ist unerheblich, siehe Abbildung 2.7. Eine solche Darstellung durch zwei Symbole oder Zustände wird binär genannt und ist vom Dualsystem – dem Zahlensystem zur Basis 2 – zu unterscheiden. Daraus abgeleitet ist bit (klein geschrieben) die Maßeinheit der Informationsmenge, seit C LAU -
122
2 Linux/UNIX
DE E LWOOD S HANNON sich in seiner Informationstheorie damit befasst hat. Der gewöhnliche Benutzer kommt mit Bits selten in Berührung. Die Übertragungsgeschwindigkeit in Netzen wird meist in Bit/s gemessen, ansonsten handhabt man die Bits zu Bytes gebündelt. Wie viele Bits ein Byte ergeben, schwankte anfangs zwischen 5 und 9. Im Jahr 1964 legte IBM im Zusammenhang mit der Rechnerfamilie IBM System/360 das Byte zu 8 Bits und noch einige andere Dinge fest, und das rührt daher, dass sich mit 8 Bits alle Schriftzeichen der westlichen Welt darstellen lassen. In Frankreich und in Normen wird das Wort Oktett bevorzugt. Speicherplatz wird in Bytes oder Blöcken davon zugewiesen. Genauso wie man Gramm in Kilogramm umrechnet, lassen sich Bytes zu Kilobytes und höheren Einheiten zusammenfassen. Neben der gewohnten Einteilung nach Zehnerpotenzen (kilo = 1000) findet sich – sogar häufiger – die Einteilung nach Zweierpotenzen (kilo = 1024). Für grobe Abschätzungen spielt der Unterschied keine Rolle, aber manchmal heißt es aufpassen. Will man ausdrücklich darauf hinweisen, dass man Zweierpotenzen meint, schreibt man KiByte oder KiB für 1024 Byte. Die nächsten Stufen sind Mega, Giga, Tera, Peta, Exa und Zetta, immer mit dem Faktor 1000 oder 1024 dazwischen, siehe http://physics.nist.gov/cuu/ Units/ und die deutsche Wikipedia unter dem Suchwort Binärpräfix. Datenspeicher im Terabyte-Bereich gibt es bereits.
Typ User −dl
Group
Others
−r−w−x −r−w−x −r−w−x
d = Verzeichnis − = Datei r = read l = Softlink w = write und andere x = execute − = nicht gesetzt und andere
Einträge
Besitzer
Gruppe
Anzahl
Name
Name
Anzahl der Verzeichniss− Einträge (Hardlinks)
Größe in Byte
Datum Datum
Dateiname Name
Datum der letzten Änderung
Abb. 2.8: Bedeutung der Ausgabe des Kommandos ls -l
Besitzer und Benutzer Die Information, ob ein Benutzer eine Datei lesen oder inhaltlich ändern darf, wird vom Betriebssystem zu jeder Datei gespeichert. Dabei wird festgehalten, wem die Datei gehört. Besitzer ist per Vorgabe der Benutzer, der die Datei erzeugt hat. Weiter wird zu jeder Datei gespeichert, was der Besitzer mit der Datei machen darf. Dabei gibt es drei verschiedene Zugriffsrechte (E: permission, F: droit d’accès): • •
Die Datei darf gelesen werden (E: read, F: lecture). Dieses Attribut wird durch den Buchstaben r abgekürzt. Die Datei darf geändert werden (E: write, F: écriture). Dieses Attribut wird durch den Buchstaben w abgekürzt.
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
•
123
Die Datei darf ausgeführt werden (E: execute, F: exécution). Dieses Attribut wird durch den Buchstaben x abgekürzt. Alle Programmdateien brauchen dieses Attribut, sowohl Skripte wie compilierte Programme.
Kopieren einer Datei setzt die Leseerlaubnis an der Datei voraus, Anlegen oder Löschen die Schreiberlaubnis im übergeordneten Verzeichnis. Bei Verzeichnissen bedeutet Ausführen Hinein- oder Hindurchgehen. Dies gilt nicht als lesender Zugriff, im Gegensatz zum Auflisten mittels ls. Textdateien können nicht ausgeführt werden, das Ausführungsrecht ist sinnlos, aber unschädlich. Gruppen Gelegentlich ist es erwünscht, einer Gruppe von Benutzern zu erlauben, auf bestimmte Dateien zuzugreifen. Hierbei kann es sich um eine Projektgruppe handeln, bei der alle Mitglieder mit denselben Daten arbeiten. Es würde dann nicht ausreichen, einzelnen Benutzern Schreib- und Leserechte an diesen Daten zu erteilen. Deswegen wird – neben dem Besitzer – für jede Datei eine Gruppe gespeichert, für die eigene Rechte gelten. Jeder Benutzer gehört mindestens einer Gruppe an (primäre Gruppe, nachzulesen in /etc/passwd). Er kann aber in weitere Gruppen (sekundäre Gruppen) wechseln (nachzulesen in /etc/group). Wenn er Dateien erzeugt, gehören diese der Gruppe, der er momentan angehört (aktive Gruppe). Hierdurch erhält er je nach Umständen das Recht, auf weitere Dateien zuzugreifen, die diesen Gruppen zugeordnet sind. Unter Debian/GNU Linux wird beim Anlegen eines Benutzerkontos gleichzeitig eine Gruppe mit demselben Namen angelegt, der der neue Benutzer automatisch angehört. Das sieht nach Verschwendung aus, hat aber den Vorteil, später alle Freiheiten bei der Gruppeneinteilung zu haben. Die für die Gruppe verwalteten Rechte sind die gleichen Rechte, wie sie auch für den Besitzer verwaltet werden, also read, write und execute. Der Besitzer einer Datei braucht nicht auch Mitglied der einer Datei verbundenen Gruppe zu sein; beide Einträge sind unabhängig voneinander. Andere Benutzer Neben den Besitzer- und Gruppenrechten werden die gleichen Rechte ein drittes Mal für alle anderen Benutzer verwaltet, also für alle, die weder der Gruppe, die mit einer Datei verbunden ist, angehören, noch Besitzer der Datei sind (Rest der Welt, E: others, F: autres), anders als bei Microsoft, wo jeder wirklich jeder ist. Viele Systemdateien auf einem Linux/UNIX-System sind für alle Benutzer lesbar, dürfen jedoch nur von Root verändert werden. Dazu gehört die Dokumentation in /usr/share/doc, die für alle Benutzer lesbar sein soll, aber von gewöhnlichen Benutzern nicht verändert werden darf, ebenso die meisten Konfigurationsdateien in /etc. Es gibt wenige Geheimnisse auf einem Linux/UNIX-System. Fast alle Programme sind von allen Benutzern ausführbar. Ein Programm kann jedoch unabhängig von den Zugriffsrechten auch selbst entscheiden, bestimmte Tätigkeiten nur auszuführen, wenn es vom Benutzer mit der Nummer 0 (Root) aufgerufen wird.
124
2 Linux/UNIX
Ändern von Dateiattributen Dateiattribute können grundsätzlich nur vom Besitzer verändert werden. Darüber hinaus hat der Systemverwalter das Recht, die Attribute aller Dateien zu verändern. Es gibt drei Kommandos zum Ändern der Attribute: • • •
chown change owner, der Besitzer einer Datei wird gewechselt, chgrp change group, die mit einer Datei verbundene Gruppe wird gewechselt, chmod change mode, die Zugriffsrechte, die Besitzer, Gruppe und andere Benutzer an einer Datei haben, werden verändert.
Im einfachsten Fall lautet die Syntax von chown: chown Benutzer Datei Damit wird der mit Benutzer angegebene Benutzer neuer Besitzer der Datei. Das Kommando chgrp wird genauso verwendet. Nur der Systemverwalter ist berechtigt, Dateien neuen Besitzern zu übertragen. Benutzer können Dateien anderen Gruppen zuordnen, wenn sie Besitzer der Datei und außerdem Mitglied der Gruppe sind. Die Änderung von Besitzer und Gruppe kann in einem Aufruf zusammengefasst werden: chown besitzer:gruppe datei Besitzer und Gruppe dürfen statt durch ihre Namen auch durch ihre IDs (Nummern) angegeben werden. Ändern Sie zur Übung (als Root, vorher su eingeben, oder mit sudo arbeiten) Besitzer und Gruppe der Datei GPL-Beer auf root, lassen Sie sich zwischendurch das Ergebnis mit ls -l anzeigen, und machen Sie die Änderungen hinterher rückgängig. Danach geben Sie die Verwalterrechte mit exit auf. Das Kommando chmod ist etwas schwieriger zu benutzen. Ihm müssen mindestens zwei Argumente übergeben werden: • •
Ein Ausdruck, der die zu erteilenden Zugriffsrechte festlegt, der oder die Namen der Datei(en), deren Zugriffsrechte geändert werden sollen.
Der Ausdruck, mit dem die Rechte festgelegt werden, setzt sich zusammen aus: •
•
Einer Angabe, wessen Zugriffsrechte (Besitzer, Gruppe oder Andere) geändert werden sollen. Diese Angabe besteht aus einem oder mehreren der folgenden Buchstaben: u Die Rechte des Besitzers (user, owner) sollen geändert werden. g Die Rechte der Gruppe (group) an der Datei sollen geändert werden. o Die Rechte aller anderen Benutzer (others) sollen geändert werden. a Die Rechte aller Benutzer (all), also Besitzer, Gruppenmitglieder und anderer Benutzer sollen gleichzeitig geändert werden. Die Angabe ist nicht zwingend. Wenn sie fehlt, wird angenommen, dass die Rechte aller Benutzer, also a, gleichzeitig geändert werden sollen. Einer Angabe, ob Rechte hinzugefügt oder entfernt werden sollen. Dabei steht + dafür, dass Rechte hinzugefügt werden sollen,
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
•
125
– dafür, dass Rechte entfernt werden sollen und = dafür, dass die Rechte exakt so wie angegeben gesetzt werden sollen. Der Angabe, welche Rechte hinzugefügt, entfernt oder gesetzt werden sollen. Hierbei werden die gleichen Abkürzungen benutzt, die wir schon aus der Ausgabe von ls -l kennen: r Leserecht. w Schreibrecht. x Recht zum Ausführen der Datei oder Durchsuchen des Verzeichnisses.
Zwischen den einzelnen Teilen eines solchen Ausdrucks darf kein Leerzeichen stehen. Der Ausdruck u+w würde bewirken, dass chmod einer Datei Schreibrechte für den Besitzer hinzufügt. Ebenso würde das Argument g+w die Schreibrechte für die Mitglieder der Gruppe, die mit der Datei verbunden ist, hinzufügen. Der Ausdruck a-r würde die Leserechte an einer Datei für Besitzer, Gruppenmitglieder und andere Benutzer entfernen. Dies ließe sich mit a+r wieder ändern. a=r würde allen Benutzern ausschließlich Leserechte erteilen und alle weiteren Rechte entfernen. Kombinationen sind möglich. So fügt der Ausdruck ug+rwx Schreib-, Leseund Ausführungsrechte hinzu, wohingegen go-rwx diese Rechte für die Gruppe und andere Benutzer entfernt. Außerdem können unterschiedliche Ausdrücke durch Kommas voneinander getrennt angegeben werden, Beispiel u+rwx,g+w,o-rwx. Um alle Rechte für alle Benutzer an der Datei GPL-Beer aufzuheben, ist im Verzeichnis texte folgendes Kommando einzugeben: joe@debian:~$ chmod a-rwx GPL-Beer Danach kann niemand (auch nicht der Besitzer) die Datei lesen oder ändern. Versuchen Sie, diese Datei mit vi zu bearbeiten; schon das Lesen scheitert. Glücklicherweise können der Besitzer der Datei sowie der Systemverwalter die Rechte wieder ändern und mit folgendem Kommando dem Besitzer Lese- und Schreibrechte einräumen: joe@debian:~$ chmod u+rw GPL-Beer Machen Sie sich jetzt mit dem Kommando chmod vertraut, indem Sie • • •
sich und der verbundenen Gruppe Ausführungsrechte an der Datei GPL-Komm geben. die Ausführungsrechte wieder aufheben. sicherstellen, dass nur Sie die beiden Dateien in dem Verzeichnis lesen dürfen.
Lassen Sie sich zwischendurch die Änderungen mittels ls -l anzeigen. Es gibt eine zweite Art, die Zugriffsrechte zu kennzeichnen, die von manchen Benutzern bevorzugt wird. Wir bilden eine dreistellige Oktalzahl auf folgende Weise: • •
Das Leserecht (r) erhält den Wert 4, das Schreibrecht (w) den Wert 2 und das Ausführungsrecht (x) den Wert 1. Eine Null bedeutet keine Zugriffsrechte. Für jeden der drei Benutzerkreise werden die jeweiligen Zugriffsrechte addiert. Das Ergebnis ist ein Wert zwischen 0 und 7, daher Oktalzahl.
126
•
2 Linux/UNIX
An vorderster (linker) Stelle steht die Summe für den Besitzer, an mittlerer Stelle die für die Gruppe und rechts die für andere Benutzer.
Gebräuchliche auf diese Weise gebildete Werte sind: • • • •
700: alle Zugriffsrechte für den Besitzer, keine Zugriffsrechte für eine Gruppe oder andere; vielfach bei Home-Verzeichnissen anzutreffen, 750: alle Zugriffsrechte für den Besitzer, Lese- und Ausführungsrecht für die Gruppe, für alle anderen keine Zugriffsrechte. 755: alle Zugriffsrechte für den Besitzer, Lese- und Ausführungsrechte für die Gruppe und alle anderen; ein häufiger Wert für Systemverzeichnisse, 640: Lesen und Schreiben für den Besitzer, Lesen für die Gruppe, für alle anderen kein Recht; sinnvoll bei Textdateien in einem Gruppenprojekt.
Der Aufruf: joe@debian:~$ chmod 600 GPL-Beer setzt die Zugriffsrechte an der Datei GPL-Beer auf Lesen und Schreiben für den Besitzer, unabhängig davon, wie sie vorher lauteten. Gelegentlich wird die oktale Ergänzung jeder Stelle benötigt und als Maske (file mode creation mask) bezeichnet, siehe man bash unter dem Suchwort umask. Die Maske zum Zugriffsrecht 750 wäre 027. Zugriffskontrolllisten Für einige Zwecke ist das Konzept von Besitzern, Gruppen und Rest der Welt nicht flexibel genug. Hier helfen Zugriffskontrolllisten (E: access control list, ACL, F: liste de contrôle d’accès) weiter. Wenn es um Dateien geht, müssen Kern und Dateisystem solche Listen unterstützen, was gegenwärtig möglich, aber nicht vorgegeben ist. Der Vorteil der Listen besteht darin, dass man mehreren Benutzern oder Gruppen genau bestimmte Rechte zuteilen kann, während das herkömmliche Owner-Group-OthersKonzept nur einen Benutzer – nämlich den Besitzer – und nur eine Gruppe zulässt. Das Debian-Paket acl bringt drei Werkzeuge zum Umgang mit Zugriffskontrolllisten von Verzeichnissen und Dateien mit. Der Aufruf: joe@debian:~$ getfacl datei zeigt die herkömmlichen Zugriffsrechte einer Datei an, falls keine Zugriffskontrolllisten eingerichtet sind. Einzelne Anwendungen verwalten eigene Zugangskontrollisten, beispielsweise der HTTP-Proxy Squid. In der Datei /etc/squid/squid.conf werden im Abschnitt ACCESSCONTROLS zuerst ACL-Klassen definiert, die aus Benutzern, Rechnern, Dateien oder anderen Objekten bestehen, beispielsweise mittels der Zeile: acl unser_netz src 192.168.1.0/24 192.168.2.0/24 Anschließend wird der Klasse ein durch einen Operator gekennzeichneter Zugriff auf einen Dienst (Protokoll) erlaubt oder verboten:
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
127
http_access allow unser_netz http_access deny all Die Zeilen werden der Reihe nach durchgegangen, bis eine zutrifft. Die Klasse all trifft immer zu; wenn nicht eine vorangegangene Zeile zu einer Entscheidung geführt hat, wird der Zugriff verwehrt. Das Konzept wird im User’s Guide von Squid umfassend erklärt. Eine ACL-Klasse ähnelt einer Rolle in einer Datenbank. Man vermeidet, dass man bei jeder Genehmigung alle Betroffenen aufzählen muss, und gewinnt an Übersicht. Zeitstempel Zu jeder Linux/UNIX-Datei gehören drei Zeitangaben, die Zeitstempel (E: timestamp, F: date) genannt und automatisch verwaltet werden: • • •
die Zeit des jüngsten lesenden oder schreibenden Zugriffs (access time, atime), die Zeit des jüngsten schreibenden Zugriffs (modification time, mtime), die Zeit der jüngsten Änderung des Datei-Status (status change time, ctime).
Ein Lesezugriff ändert nur den ersten Stempel (atime). Ein schreibender Zugriff aktualisiert die beiden ersten Stempel (atime und mtime), weil dem Schreibvorgang ein Lesevorgang vorausgeht. Der Datei-Status umfasst den Besitzer samt Gruppe, die Zugriffsrechte und den Linkzähler, also Informationen aus dem Inode. Schreiben, eine Änderung der Zugriffsrechte mittels chmod oder das Hinzufügen von harten Links mittels ln ändert den dritten Stempel. Kurz und bündig ist das auf der Manualseite zum Systemaufruf stat (Sektion 2) nachzulesen. Mittels des Kommandos ls kann man sich die Zeitstempel ansehen (man sollte auch einmal mit einer temporären Datei alle Änderungen durchspielen): • • •
ls -l zeigt die Zeit des jüngsten modifizierenden Zugriffs auf die Datei an (mtime), wichtig, ls -lu zeigt die Zeit des jüngsten Zugriffs auf die Datei an (atime), ls -lc zeigt die Zeit der jüngsten Statusänderung (Inhalt, Besitzer, Zugriffsrechte, Links) an (ctime).
Auf manchen Linuxen findet sich ein Kommando stat, das den Systemaufruf stat umhüllt und die Informationen aus dem Inode – darunter die Zeitstempel – lesbar wiedergibt. Der Zeitpunkt der Erschaffung einer Datei – das wäre der allererste schreibende Zugriff – wird nicht festgehalten und ist auch aus technischer Sicht uninteressant. Nur wenn eine Datei seit ihrer Erzeugung nicht verändert worden ist, deckt sich die mtime mit dem Entstehungsdatum. Änderungen am Datei-Inhalt hingegen sind für Werkzeuge wie make und für Backups wichtig, um entscheiden zu können, ob eine Datei aktuell ist. 2.8.4 Gerätedateien Eine Besonderheit von Linux/UNIX ist das Ansprechen aller Geräte – vom Speicher bis zum Drucker und virtuellen Geräten – als Datei. Das erleichtert das Programmieren. Die Gerätedateien (E: special device file, F: fichier périphérique special) sind
128
2 Linux/UNIX
ausnahmslos im Verzeichnis /dev zusammengefasst. Dort stehen mehr Gerätedateien, als Ihr Rechner Geräte hat. Das ist unschädlich, kostet kaum Speicherplatz und bereitet das Hinzufügen weiterer Geräte vor. Mittlerweile ermöglicht das Dynamic Device Management mittels udev das dynamische Anlegen und Löschen von Gerätedateien nach Bedarf. Falls auf Ihrem System ein udev-Dämon werkelt, ist dieser Weg eingerichtet. Bei den Geräten wird zwischen zeichenweise (character device, raw device) und blockweise (block device) arbeitenden unterschieden. CharacterGeräte lesen und schreiben Daten ungepuffert in Päckchen beliebiger Größe, Blockgeräte gepuffert in Blöcken fester Größe. Eine Festplatte beispielsweise fällt in beide Gruppen und hat daher zwei Gerätedateien für die beiden Zugriffsarten. Wie in anderen Verzeichnissen gibt es in /dev Unterverzeichnisse und symbolische Links. Bei den Namen der Gerätedateien erspart man sich Mühe, wenn man sich an die Konventionen hält, siehe Tabelle 23.1 im Anhang auf Seite 1065. Zu jedem Gerät gehört ein Zahlenpaar, das mit dem Kommando ls -l angezeigt wird. Um die Gerätenummer der Partition /dev/hda2 zu erfahren, geben wir ein: joe@debian:~$ ls -l /dev/hda2 Die Ausgabe sieht ungefähr folgendermaßen aus: brw-rw----
1 root
disk
3,2
2006-11-21 /dev/hda2
Das Zahlenpaar, die Gerätenummer, wird mit den beiden Zahlen zwischen Gruppe und Datum angegeben. Sie lautet für /dev/hda2 also 0302 (führende Nullen werden von ls nicht ausgegeben, aber beispielsweise von /sbin/lilo benutzt, um Backup-Dateien eindeutig zu benennen). Man unterscheidet zwei Gerätenummern: Major- und Minor-Nummer. Die erste Zahl in der Ausgabe von ls ist die MajorNummer, die zweite die Minor-Nummer. Die Major-Nummer kennzeichnet den Gerätetreiber, die Minor-Nummer die Instanz (die laufende Nummer oder bestimmte Parameter) des jeweiligen Treibers. So hat beispielswesie /dev/hda7 die MajorNummer 3 (Treiber) und die Minor-Nummer 7 (laufende Nummer der Partition). Eine sehr nützliche Gerätedatei ist /dev/null, der Bit Bucket oder das Schwarze Loch im Datenall. Hinter der Datei steckt keine Hardware. Alles, was nach /dev/null geschrieben wird, ist weg. Liest man aus /dev/null, so erhält man ein End-of-File-Zeichen (EOF) zurück. Kopieren wir /dev/null in eine richtige Datei: joe@debian:~$ cp /dev/null unsere_datei so wird sie geleert, bleibt aber erhalten, im Gegensatz zum Löschen. Mit dem Aufruf: joe@debian:~$ cat /dev/zero > /dev/null beschäftigt man den Zentralprozessor mit einer sinnlosen Arbeit; beenden mit+ . Es fällt auf, dass Netzschnittstellen wie eth0 nicht im /dev-Verzeichnis auftauchen. Treiber für Netzschnittstellen haben es weder mit Zeichen noch mit Blöcken zu tun, sondern mit Datenpaketen. Wenn Pakete eintrudeln, informieren sie den Kern
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
129
von sich aus, was einer Festplatte nie einfiele. Die Treiber bilden eine Klasse für sich. Entsprechend werden die Netzschnittstellen nicht in /proc/devices aufgelistet, sondern in /proc/net/dev. Während man aus Gerätedateien lesen oder in sie schreiben kann: joe@debian:~$ cat < /dev/null ist dieser einfache Weg bei Netzschnittstellen nicht gangbar. Wer Genaueres wissen will, lese in der Literatur zur Programmierung von Treibern und Kernmodulen nach. 2.8.5 Links (Verweise, Verknüpfungen) Eine Besonderheit der UNIX-Dateisysteme stellen die Verweise oder Links (E: link, F: lien) dar. Man unterscheidet zwei Typen, die weichen Verweise (softlink) und die harten Verweise (hardlink). Softlinks werden auch als symbolische Links (symbolic link, Symlink) bezeichnet; in anderen Welten ist die Bezeichnung Verknüpfung üblich. Harte Links Ein harter Link ist ein Eintrag einer Datei in einem Verzeichnis, also das Paar InodeNummer und Dateiname. Für jede Datei muss es mindestens einen Verzeichniseintrag geben, damit sie überhaupt gefunden wird. Namenlose Dateien kommen nicht vor. Der Zugriff auf eine Datei mittels ihrer Inode-Nummer ist zwar möglich, aber im Normalbetrieb nicht vorgesehen. Löschen einer Datei bedeutet das Entfernen ihres Eintrags aus dem Verzeichnis. Die Daten mögen noch auf der Platte sein, aber sie sind nicht mehr zugänglich. Deshalb wird der Vorgang genauer als logisches Löschen bezeichnet. Der ehemals von der Datei auf der Platte beanspruchte Platz wird vom System als frei betrachtet und kann jederzeit überschrieben werden. Ein Zugriff auf Dateien, die keinen Namen mehr haben, ist nur mit Werkzeugen möglich, die in das Dateisystem eingreifen (file system debugger, disc editor), und setzt vertiefte Kenntnisse und Glück voraus. Es ist möglich, für dieselbe Datei sprich Inode-Nummer weitere Einträge im selben, aber auch in anderen Verzeichnissen anzulegen. Hierzu dient das Kommando ln. Er hat die gleiche Syntax wie die Kommandos cp und mv, nämlich: ln Quelle Ziel Stellen Sie sich vor, Sie bearbeiten die Datei GPL-Beer so häufig, dass Sie sie gleich nach der Anmeldung in Ihrem Home-Verzeichnis vorfinden wollen. Trotzdem soll Sie auch im Verzeichnis texte liegen, weil sie zu Ihren Textdateien gehört. Wenn Sie sich nun in diesem Unterverzeichnis Ihres Home-Verzeichnisses befinden, erzeugen Sie mit folgendem Kommando einen weiteren Verzeichniseintrag für die Datei GPL-Beer in Ihrem Home-Verzeichnis: joe@debian:~/texte$ ln GPL-Beer ../GPL-Beer oder aus Ihrem Home-Verzeichnis heraus:
130
2 Linux/UNIX
joe@debian:~$ ln texte/GPL-Beer GPL-Beer Die Datei hat jetzt in Ihrem Home-Verzeichnis den gleichen Namen wie im Verzeichnis texte. Sie hätten aber auch einen anderen Namen wählen können. Wechseln Sie nun in Ihr Home-Verzeichnis und ändern Sie die dortige Datei GPL-Beer geringfügig mit einem Texteditor. Wechseln Sie danach wieder in das Verzeichnis texte und sehen Sie nach, ob die Änderungen auch dort in GPL-Beer vorhanden sind. Sie sehen, es handelt sich wirklich um dieselbe Datei. Logischerweise gehören zu beiden Dateinamen dieselben Attribute, da ja nur eine Datei und ein Inode dahinter stecken. Die Anzahl der harten Links auf eine Datei wird im Linkzähler festgehalten, dessen Wert von ls -l in der zweiten Spalte angezeigt wird. Herauszufinden, wo etwaige weitere Namen einer Datei liegen, kann mühsam werden, man muss mit find und der Inode-Nummer arbeiten. Der Inode weiß zwar, wieviele harte Links auf ihn verweisen, aber nicht, wo diese liegen. Links im selben Verzeichnis lassen sich manchmal an Hand der übereinstimmenden Dateigröße erkennen. Wenn Sie jetzt einen der beiden Verzeichniseinträge mit rm löschen, so können Sie auf die Datei immer noch unter dem anderen Verzeichniseintrag zugreifen. Erst wenn alle Verzeichniseinträge gelöscht sind – im Beispiel GPL-Beer in Ihrem Home-Verzeichnis und im Verzeichnis texte – kann auf die Datei nicht mehr zugegriffen werden, und der Platz, den sie auf der Festplatte eingenommen hat, wird vom Betriebssystem anderweitig vergeben. Beim Erzeugen von harten Links bestehen zwei Einschränkungen. Erstens lassen sich keine Hardlinks auf Verzeichnisse anlegen, weil dies zu geschlossenen Wegen im Verzeichnisbaum führen kann. Ausnahmen sind die vom System angelegten Bezeichnungen Punkt und Punkt-Punkt für das aktuelle Verzeichnis und seinen Boss. Zweitens müssen sich harte Links immer auf demselben Dateisystem befinden wie die Datei (Inode) selbst. Jedes Dateisystem führt eigene Inode-Listen. Deshalb kann ein harter Link nicht die Grenze eines Dateisystems überschreiten. Weiche Links (Symlinks) Die beiden Einschränkungen für harte Links lassen sich durch weiche oder symbolische Links (Softlink, Symlink) umgehen. Weiche Links sind kleine Dateien mit eigener Inode-Nummer, in denen die Anweisung an das System steht, eine andere Datei zu verwenden, sobald auf den weichen Link zugegriffen wird. Linux-Dateisysteme wie ext2 und ext3 speichern den Zielnamen in dem Inode des Links, sofern er nicht länger ist als 60 Zeichen, und sparen so einen Speicherblock und einen Lesezugriff (fast symbolic link). Weiche Links können auch für Verzeichnisse angelegt werden, sie dürfen die Grenzen eines Dateisystems überschreiten, und sie dürfen geschachtelt oder verkettet werden. Existiert das Ziel eines weichen Links nicht mehr, führt sein Aufruf zu einer Fehlermeldung. Auch weiche Links werden mit dem Kommando ln erzeugt, dem dann die Option -s (symbolic) mitgegeben wird. Die Syntax zum Erzeugen eines weichen Links lautet: ln -s Quelle Ziel
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
131
Um einen weichen Link mit dem Namen Link-auf-GPL-Beer in Ihrem HomeVerzeichnis auf die Datei GPL-Beer im Unterverzeichnis texte zu erzeugen, ist folgendes Kommando einzugeben: joe@debian:~/texte$ ln -s ~/texte/GPL-Beer ~/Link-auf-GPL-Beer Ebenso wie bei einem harten Link können Sie jetzt mit Link-auf-GPL-Beer genauso arbeiten wie mit der Originaldatei. Es werden in jedem Fall dieselben Daten bearbeitet. Ein Unterschied zwischen weichen und harten Verweisen besteht aber in der Auswirkung des Löschens auf Original und Verweis. Während das Löschen des Ursprungseintrags bei einer zweifach hart gelinkten Datei die Daten nicht löscht, weil sie noch unter dem zweiten Namen zu erreichen ist, sieht es bei weichen Links anders aus. Hier ist zwischen der Entfernung des Links und Entfernung der Ursprungsdatei zu unterscheiden. Die Zugriffsrechte von Symlinks werden nicht beachtet, sondern nur die Zugriffsrechte der Zieldatei. Will man dennoch die Besitzverhältnisse eines Symlinks ändern (und nicht die der Zieldatei), bieten chown und chgrp entsprechende Optionen. Ein sinnvoller Anwendungsbereich für die Arbeit mit weichen Links ist folgende Situation: Sie haben auf der Partition, auf der Sie Debian eingerichtet haben, keinen Platz mehr und möchten zusätzliche Pakete einrichten. Deswegen bauen Sie eine weitere Festplatte in Ihren Rechner ein. Unter anderen Betriebssystemen würde dies oft bedeuten, dass Sie nun das Betriebssystem und alle Anwendungen neu einrichten müssten. Unter Linux/UNIX kopieren Sie einfach einen Teil der Dateien und Verzeichnisse auf die neue Festplatte und löschen sie anschließend auf der alten Platte. Dann erzeugen Sie weiche Links, die von der Position, wo sich die Dateien vorher befanden, auf die neue Position zeigen. In der Praxis würde man hier einfach ein ganzes Verzeichnis mit allen Unterverzeichnissen auf die neue Platte kopieren und dann einen einzigen weichen Link auf die neue Position dieses Verzeichnisses einrichten. Der Verwalter benutzt Symlinks auch oft, um Verzeichnissen oder Dateien weitere Zugangswege zu geben, da sie von verschiedenen Programmen an verschiedenen Stellen im Dateisystem erwartet werden. Beispielsweise erzeugt debian:/# ln -s media/cdrom cdrom einen Zugangsweg zum Einhängepunkt /media/cdrom direkt in der Wurzel des Dateibaums oder (einzeilig ohne Zwischenraum nach der Null): debian:/usr/local/bin# ln -s /usr/local/Adobe/Acrobat7.0 /bin/acroread acroread einen Zugangsweg zum Acrobat Reader in einem Verzeichnis, das üblicherweise im Befehlspfad (PATH-Variable) der Benutzer vorkommt.
132
2 Linux/UNIX
2.8.6 Pfade Absolute Pfade Ein Pfad (E: path, F: chemin) ist der Weg im Dateibaum von einem Ausgangspunkt zu einem Verzeichnis oder einer Datei. Wenn man nur kurz etwas in einem Verzeichnis ändern möchte, ist es oft zu aufwendig, in dieses Verzeichnis zu wechseln, dort etwas auszuführen und dann zurück in das alte Verzeichnis zu wechseln. Man kann sich dann mit dem absoluten Pfadnamen helfen. Beispielsweise möchten Sie ein drittes Verzeichnis in Ihrem Home-Verzeichnis anlegen, das den Namen sound trägt, und Sie befinden sich noch im Verzeichnis /home/joe/grafik. Mit der eben beschriebenen Methode würden Sie jetzt zurück in Ihr Home-Verzeichnis wechseln, dort den Befehl mkdir sound eingeben und dann wieder zurück in das Verzeichnis grafik wechseln. Sie können aber auch folgenden Befehl eingeben: joe@debian:~/grafik$ mkdir /home/joe/sound Der absolute Pfadname bezeichnet den vollen Pfad mit allen Verzeichnissen und Unterverzeichnissen ausgehend von der Wurzel des Dateisystems, beginnt also immer mit einem Schrägstrich. Relative Pfade Manchmal hat aber auch die Verwendung absoluter Pfadnamen Nachteile. Stellen Sie sich vor, Sie verweilen in Ihrem Home-Verzeichnis und möchten im dort befindlichen Verzeichnis briefe zwei Unterverzeichnisse mit den Namen privat und geschaeft anlegen. Sie könnten in das Verzeichnis briefe wechseln und die Unterverzeichnisse dort erzeugen. Sie können die Verzeichnisse aber auch mit folgendem Befehl anlegen: joe@debian:~$ mkdir briefe/privat briefe/geschaeft Jetzt haben Sie dem Befehl mkdir zwei Parameter übergeben, wodurch beide Verzeichnisse mit einem Befehl erzeugt wurden. Viele Linux/UNIX-Befehle akzeptieren solche Mehrfach-Parameter. Außerdem haben Sie die beiden Verzeichnisse privat und geschaeft erzeugt, indem Sie den relativen Pfad, nämlich den Pfad ausgehend vom Arbeitsverzeichnis angegeben haben. Relative Pfadnamen bezeichnen einen Pfad ausgehend vom Arbeitsverzeichnis und beginnen daher nie mit einem Schrägstrich. Wenn Sie jetzt in das Verzeichnis musik wechseln und sich den Inhalt von briefe anzeigen lassen wollen, können Sie folgenden Befehl eingeben: joe@debian:~$ ls ../briefe Auch dies ist ein relativer Pfadname. Mit .. (zwei Punkte) wird das über dem Arbeitsverzeichnis liegende Verzeichnis bezeichnet – unabhängig von seinem Namen – das hier das Home-Verzeichnis ist. Von dort geht es weiter in das Verzeichnis briefe.
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
133
2.8.7 Besondere Dateiarten Benannte Pipes (FIFO) In der Auflistung von Verzeichnissen tauchen – selten – zwei Dateiarten auf, die der Kommunikation zwischen Prozessen dienen: benannte Pipes und Sockets, gekennzeichnet durch p bzw. s an erster Stelle der Ausgabezeilen. Unbenannte Pipes kennen wir bereits aus Abschnitt 2.7.2 Kommandoverkettung (Pipe) auf Seite 66. Benannte Pipes (E: named pipe, F: tube nommé) tragen einen Namen wie eine Datei, werden auch als FIFO bezeichnet und mit dem Kommando mkfifo angelegt: joe@debian:~$ mkfifo meine_benannte_pfeife Alternativ geht auch: joe@debian:~$ mknod meine_benannte_pfeife p Während die unbenannte Pipe mit den beteiligten Prozessen lebt und stirbt, ist die benannte Pipe eine selbständige Einrichtung. Ihr zweiter Name FIFO bedeutet First In First Out und kennzeichnet einen Speichertyp, bei dem die zuerst eingelagerten Daten auch als erste wieder herauskommen (im Gegensatz zum Stack, Stapel oder Keller, bei dem die zuletzt eingelagerten Daten als erste wieder herauskommen). Beide Pipes speichern ihre Daten im Arbeitsspeicher, zur benannten gehört ein Inode ohne Datenblöcke. Mittels ls -l überzeugen wir uns von ihrer Existenz. Dann können wir mit: joe@debian:~$ who > meine_benannte_pfeife & joe@debian:~$ cat < meine_benannte_pfeife unsere Pipe zum Datentransport vom ersten zum zweiten Prozess einsetzen. Die Reihenfolge der Daten ist durch die Eingabe festgelegt, beim Auslesen verschwinden die Daten aus der Pipe (kein Kopieren). Je nach Zugriffsrechten können der schreibende und der lesende Prozess verschiedenen Benutzern gehören, was bei einer unbenannten Pipe schlecht möglich ist. Die Pipe existiert vor und nach den beiden Prozessen und ist beliebig weiter verwendbar. Man wird sie mit: joe@debian:~$ rm meine_benannte_pfeife wieder los, wie eine gewöhnliche Datei. Sockets Sockets (BSD-Socket, wörtlich Fassung, Steckdose, wird nicht übersetzt, auch nicht in Frankreich) sind ein Mechanismus zur Kommunikation zwischen zwei Prozessen auf derselben oder auf vernetzten Maschinen in beiden Richtungen. Die SocketSchnittstelle besteht aus einer Handvoll von Systemaufrufen, die von Benutzerprozessen in einheitlicher Weise verwendet werden. Dem Programmierer stellen sich Sockets wie Dateien dar: Er öffnet mittels eines Systemaufrufes einen Socket, erhält einen Socket-Deskriptor zurück und schreibt nach dort oder liest von dort. Ist er
134
2 Linux/UNIX
fertig, schließt er den Socket. Zu einem Socket gehört ein Inode ohne Datenblöcke. Unter den Sockets liegen die Protokollstapel, das heißt die Programmmodule, die die Daten entsprechend den Schichten eines Netzprotokolls aufbereiten, und schließlich die Gerätetreiber für die Netzkarten oder sonstige Verbindungen. Näheres siehe mittels man 7 socket. Tab. 2.3: Die von ext2-Dateisystemen verwendeten Dateitypen File_type
Beschreibung
0 1 2 3 4 5 6 7
Unbekannter Typ (sollte nicht vorkommen) Gewöhnliche Datei, mit Inode und Datenblöcken Verzeichnis, mit Inode und Datenblöcken zeichenweise arbeitendes Gerät, nur Inode, keine Datenblöcke blockweise arbeitendes Gerät, nur Inode, keine Datenblöcke benannte Pipe (FIFO), nur Inode, keine Datenblöcke Socket, nur Inode, keine Datenblöcke symbolischer Link, mit Inode und gegebenenfalls Datenblock
Sockets lassen sich durch Streams nachbilden (emulieren), aber darauf einzugehen würde zu weit führen. Das betrifft Programmierer, und auch nicht alle. Der Verwalter kommt unter Umständen beim Konfigurieren von Netzdruckern mit Sockets in Berührung. Im Verzeichnis /tmp sind gelegentlich Sockets zu besichtigen. Tabelle 2.3 zeigt alle Dateitypen, genau genommen nur für ext2-Dateisysteme. Andere Dateisysteme können weitere Dateitypen festlegen. 2.8.8 Arbeiten mit Dateien Anzeigen des Inhalts Um den Inhalt einer Textdatei auf dem Bildschirm auszugeben, kann das Kommando cat (concatenate) benutzt werden. Es ist ein einfaches Filter, das von stdin liest und nach stdout schreibt, weiter nichts. Wegen seiner Einfachheit ist es manchmal die letzte Rettung für den Verwalter, wenn richtige Texteditoren oder Betrachter nicht verfügbar sind. Beispielsweise kann man mit cat eine leicht beschädigte fstab reparieren, falls der Texteditor vi nicht zu erreichen ist. Auf jedem Debian-System befindet sich die Datei /etc/debian_version, die Sie sich mit dem Kommando: joe@debian:~$ cat /etc/debian_version anzeigen lassen können. Es erscheint eine Zeichenkette, die der Version Ihrer Debian-Distribution entspricht und in dieser Datei gespeichert ist. Geben Sie nun folgendes Kommando ein: joe@debian:~$ cat /usr/share/common-licenses/GPL
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
135
Es wird der Text der GNU General Public License auf dem Bildschirm ausgegeben. Bei der Anzeige tritt ein Problem auf: Der Text ist so lang, dass er oben aus dem Bildschirm herausläuft und Sie nur den Schluss lesen können. Man braucht ein Werkzeug, mit dem auch größere Dateien am Bildschirm gelesen werden können. Dieses Werkzeug wird durch das Programm more oder seinen leistungsfähigeren Nachfolger less dargestellt. Geben Sie folgendes Kommando ein: joe@debian:~$ less /usr/share/common-licenses/GPL Sie sehen jetzt den Anfang der Datei auf dem Bildschirm und können mit den Tasten <Page Up> und <Page Down> sowie mit den Pfeiltasten in der Datei blättern. Ein besonderer Vorteil von less besteht darin, dass auch nach Zeichenketten gesucht werden kann. Dazu ist die Taste > (Schrägstrich) zu drücken, die zu suchende Zeichenkette einzugeben und dann mitzu bestätigen. Suchen Sie einmal nach dem Begriff software in dieser Datei. Durch Betätigen der Taste (next) gelangen Sie nach dem Suchvorgang an die jeweils nächste Stelle des Textes, an der der Begriff gefunden wird. Der Suchbegriff wird als regulärer Ausdruck interpretiert, sodass Sie auch Suchmuster eingeben können. Zum Verlassen drücken Sie die Taste (quit). Das Programm wird ausführlich in Abschnitt 2.11.3 Benutzung von less auf Seite 166 beschrieben. Weitere Textbetrachter sind most und xless. Letzteres setzt X11 voraus. Der unter Linux am häufigsten benutzte Betrachter dürfte less sein. Kopieren Weil Sie sich als gewöhnlicher Benutzer am System angemeldet haben, dürfen Sie die Datei /usr/share/common-licenses/GPL nicht ändern. Sie ist Bestandteil der Debian-GNU/Linux-Installation und kann nur vom Systemverwalter geändert werden. Außerdem wäre es töricht, diese Datei zu ändern, weil sie vom Paketverwaltungssystem gepflegt und während einer Aktualisierung des Systems unter Umständen überschrieben wird, wodurch Ihre Änderungen verloren gingen. Wenn Sie die Datei trotzdem bearbeiten möchten – etwa um sie mit Anmerkungen zu versehen – kopieren Sie sie in Ihr Home-Verzeichnis. Zum Kopieren von Dateien dient das Kommando cp (copy). Dem Kommando müssen mindestens zwei Argumente übergeben werden, nämlich der Name der Datei, die kopiert werden soll (Quelldatei) und der Name der zu erstellenden Datei, in die der Inhalt der Quelldatei kopiert werden soll (Zieldatei). Das Ziel kann auch ein Verzeichnis sein. Dann wird die Zieldatei in dem angegebenen Verzeichnis mit dem Namen der Quelldatei erzeugt. Die Syntax von cp lautet im einfachsten Fall: cp Quelle Ziel Um jetzt die GPL in Ihr Home-Verzeichnis zu kopieren, geben Sie folgendes Kommando ein: joe@debian:~$ cp /usr/share/common-licenses/GPL ~/
136
2 Linux/UNIX
Hier verwenden wir die Abkürzung ~, um das eigene Home-Verzeichnis anzugeben. Statt der Tilde können Sie auch einen Punkt verwenden; dann landet die Kopie im Arbeitsverzeichnis. Wenn Sie sich jetzt in Ihrem Home-Verzeichnis befinden (überprüfen mit pwd), können Sie sich Ihre persönliche Kopie der Datei ansehen. Die Kopie gehört Ihnen, Sie haben alle Rechte an ihr. Außerdem möchten Sie vielleicht neben der Originalversion eine weitere Version, in die Sie Ihre Kommentare einfügen, in Ihrem Home-Verzeichnis haben. Dazu verwenden Sie folgendes Kommando: joe@debian:~$ cp GPL GPL-Komm Voraussetzung für die korrekte Ausführung des Kommandos ist, dass Sie sich weiterhin in dem Verzeichnis befinden, wo die kopierte Datei GPL liegt. Wenn Sie sich jetzt mit ls den Inhalt des Verzeichnisses anzeigen lassen, sollten Sie beide Kopien der GPL sehen. Bei den Argumenten von cp und ähnlichen Kommandos ist Aufmerksamkeit geboten. Nehmen wir an, wir hätten das Verzeichnis /home als Arbeitsverzeichnis gewählt. Dann kopiert der Aufruf: joe@debian:/home$ cp -dpr joe /tmp Joes Home-Verzeichnis joe samt seinem Inhalt rekursiv (mit allen Unterverzeichnissen) in das Verzeichnis /tmp, wobei Symlinks und Attribute erhalten bleiben. Wir finden nach Abschluss des Kopiervorgangs ein Verzeichnis /tmp/joe vor. Der Aufruf: joe@debian:/home$ cp -dpr joe/* /tmp dagegen kopiert den Inhalt des Verzeichnisses joe (Dateien und Unterverzeichnisse) in das Verzeichnis /tmp. Wir treffen Joes Dateien und Unterverzeichnisse direkt im Verzeichnis /tmp an. Das ist logisch, aber man denkt nicht immer daran. Löschen Dateien werden mit dem Kommando rm (remove) gelöscht. Das Kommando benötigt mindestens ein Argument, nämlich den Namen der zu löschenden Datei. Es ist erlaubt, mehrere Dateien anzugeben. Die Syntax lautet im einfachsten Fall: rm Dateiname Löschen Sie die Datei GPL in Ihrem Home-Verzeichnis und überprüfen Sie mittels ls, ob die Datei verschwunden ist. Vorsich beim Gebrauch von Jokerzeichen in Verbindung mit dem Löschkommando; leicht erwischt man die falschen Dateien. Mit der Option -i fragt das Kommando vor jedem Löschen einer Datei zurück. Mit der Option -r arbeitet das Werkzeug rekursiv. Falls Sie eine Datei mit einem Namen löschen wollen, der Sonderzeichen enthält, versuchen Sie, den Namen mit Jokerzeichen einzugrenzen, und rufen rm mit der Option -i (interactive) auf, im ungünstigsten Fall: joe@debian:~$ rm -i *
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
137
wobei Sie die Fragen von rm so lange verneinen, bis sie bei dem Löschkandidaten angelangt sind. Nach dem Löschen brechen Sie das Kommando mit den Tasten+ ab. Das Debian-Paket perforate enthält drei Werkzeuge, um der Verschwendung von Plattenplatz entgegenzuwirken: • • •
finddup durchsucht ein Verzeichnis rekursiv nach doppelt vorhandenen Dateien und ersetzt auf Wunsch die Duplikate durch harte Links, findstrip durchsucht ein Verzeichnis rekursiv nach ungestrippten Programmen und gibt ihre Namen aus; vergleiche man strip, zum zieht Folgen von Nullen in Dateien zusammen.
Man sollte die Werkzeuge zuerst an Testdateien ausprobieren, um ihre Wirkung kennen zu lernen. Am brauchbarsten scheint finddup zu sein. Der Aufruf: joe@debian:~$ finddup --noactive ist ungefährlich und rödelt je nach Umfang des Arbeitsverzeichnisses eine Zeitlang auf der Platte herum. Unter Linux/UNIX sind mittels rm gelöschte Dateien weg. Sie stehen nicht mehr in einem Papierkorb oder über ein undelete-Kommando zur Verfügung. Genau genommen sind die Dateien aber nur logisch gelöscht; die Daten können unter Umständen auf einem mühsamen Weg teilweise rekonstruiert werden (File System Debugger). Wenn Sie aus Gründen der Sicherheit eine Datei, ein Verzeichnis, eine Partition oder die Daten einer ganzen Platte restlos vernichten wollen, müssen Sie sie vor dem Löschen mehrmals mit Unsinn überschreiben: debian:~# dd if=/dev/urandom of=/dev/hdb bs=64k Der Aufruf schreibt Zufallszahlen auf die gesamte Platte /dev/hdb, also die Slave-Platte am ersten IDE-Controller. Das Kommando wipe aus dem gleichnamigen Debian-Paket, Heimathafen http://abaababa.ouvaton.org/, unterstützt das Löschen durch mehrfaches Überschreiben. In dem Paket coreutils ist das GNU-Werkzeug shred enthalten, das Ähnliches leistet. Lesen Sie die zugehörigen Manualseiten gründlich, wipe und shred haben Einschränkungen. Weitere Werkzeuge zum sicheren Löschen bringt das Paket secure-delete, Heimathafen http://www.thc.org/, mit: • • • •
sfill reinigt unbelegten Platz in einer Partition, das heißt überschreibt die Reste einschließlich der Inodes nur logisch gelöschter Dateien. smem löscht den Arbeitsspeicher. srm ein gründlich arbeitender Ersatz für rm. sswap reinigt die Swap-Partition.
Nach getaner Arbeit lässt sich – möglichst im Single User Modus – mit dem Aufruf: debian:~# sfill -lf /tmp 2>/dev/null ein Verzeichnis – hier /tmp – von den Resten mittels rm logisch gelöschter Dateien befreien. Noch gültige Dateien oder Verzeichnisse werden nicht angetastet. Die
138
2 Linux/UNIX
Kommandos brauchen Zeit und werden daher oft mit Optionen zur Abkürzung des Verfahrens aufgerufen. Moderne Festplatten blenden defekt gewordene Sektoren unbemerkt aus; auf diesen können sich Daten befinden, die mit keinem Kommando mehr erreichbar sind, wohl aber mit speziellen Werkzeugen, die bis hin zu besonderen Mikroskopen reichen. Als absolut sicher gilt Einschmelzen des Datenträgers. Denken Sie daran vor dem Entsorgen oder Veräußern von Rechnern, Platten, Floppies oder USB-Stöpseln. Auch Anrufbeantworter, Mobiltelefone und Digitalkameras enthalten Speicher. Wenn Sie auf einer grafischen Benutzeroberfläche wie GNOME oder KDE mit einem Datei-Manager (Datei-Browser) arbeiten, löschen Sie eine Datei durch Anklicken und Auswählen eines Menüpunktes wie In den Müll verschieben. In diesem Fall lässt sich die gelöschte Datei wieder aus dem Mülleimer hervorholen, solange die Müllabfuhr den Eimer noch nicht geleert hat. Verschieben und Umbenennen Hoffentlich möchten Sie Ihr Home-Verzeichnis ordentlich gestalten. Deswegen soll die Datei GPL-Komm in einem Unterverzeichnis liegen, das den Namen texte trägt. Legen Sie dieses Verzeichnis jetzt an (make directory): joe@debian:~$ mkdir texte Nun kann die Datei mit dem Kommando mv (move) in dieses Verzeichnis verschoben werden. Das Kommando benötigt – wie cp – mindestens zwei Argumente, nämlich den alten Namen der zu verschiebenden Datei und den neuen. Bei dem neuen Namen kann es sich um ein Verzeichnis handeln. Dann wird die zu verschiebende Datei in das angegebene Verzeichnis gelegt und behält ihren Namen, den sie vorher – im alten Verzeichnis – hatte. Die Syntax von mv lautet im einfachsten Fall: mv Name-Alt Name-Neu Um die Datei GPL-Komm in das frisch angelegte Verzeichnis texte zu verschieben, ist folgendes Kommando einzugeben: joe@debian:~$ mv GPL-Komm texte Dass texte ein Verzeichnis und nicht etwa ein neuer Dateiname ist, erkennt das Kommando. Wechseln Sie danach in das Verzeichnis texte und überprüfen Sie, ob die Datei dort angekommen ist: joe@debian:~$ cd texte; ls -l Bleibt eine Datei beim Verschieben in ihrem Dateisystem, behält sie ihre Inodenummer. Wechselt sie das Dateisystem, entspricht das Verschieben einem Kopieren mit anschließendem Löschen der ursprünglichen Datei.
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
139
Versteckte Dateien (Punktdateien) Eine besondere Form von Dateien sind die versteckten Dateien oder Verzeichnisse (E: hidden file, hidden directory), auch als Punktdateien oder Dotfiles bzw. directories bezeichnet, weil ihr Name mit einem Punkt beginnt. Diese Dateien oder Verzeichnisse unterscheiden sich von anderen nicht bezüglich ihrer Attribute. Sie sind in keiner Weise geheim oder verschlüsselt. Solche Dateien oder Verzeichnisse werden von ls oder von Dateimanagern nur mit der Option -a (all) angezeigt. Der Grund hierfür besteht darin, dass in den Home-Verzeichnissen eine Reihe von Dateien liegt, in denen die Einstellungen der Benutzer für verschiedene Programme gespeichert werden. Dadurch können verschiedene Benutzer unterschiedliche Einstellungen für dieselben Programme verwenden. Auch wenn man ein Werkzeug nur einmal ausprobiert hat, bleibt seine persönliche Konfigurationsdatei bestehen. Das trifft sogar dann zu, wenn der Verwalter das Werkzeug wieder aus dem System rauswirft. Man sollte von Zeit zu Zeit vorsichtig unter seinen Punktdateien und -verzeichnissen ausmisten. Die Tatsache, dass eine Datei oder ein Verzeichnis leer ist, sagt nichts über ihre Daseinsberechtigung. Eine leere Datei kann allein schon durch ihre Existenz oder ihre Zeitstempel etwas bedeuten. Die Anzeige dieser Dateien, von denen bei Benutzung vieler Werkzeuge eine ganze Menge vorhanden sind, stört jedoch einfach, wenn man sich den Inhalt seines Home-Verzeichnisses ansieht. Aufgrund der großen Anzahl von Dateien wird die Struktur unübersichtlich, und gesuchte Dateien lassen sich nur schwer finden. Wenn Sie in Ihr Home-Verzeichnis wechseln und dann das Kommando: joe@debian:~$ ls -a eingeben, sehen Sie, dass dort einige solcher Dateien oder Verzeichnisse existieren. Sie sollten zumindest die Einstellungsdateien der bash finden, die die Namen .bashrc und .bash_profile tragen. Außerdem sehen Sie noch zwei besondere Einträge, die .. und . lauten. Diese Einträge bezeichnen stets das Verzeichnis, das über dem angezeigten Verzeichnis liegt (..) sowie das angezeigte Verzeichnis selbst (.), das ebenfalls einen Eintrag in der Dateiliste des betreffenden Verzeichnisses hat. Selbstverständlich ist es möglich, verschiedene Parameter beim Aufruf von ls und anderen Kommandos oder Programmen zu kombinieren. Das folgende Kommando zeigt die vollständige Liste aller Dateien in Ihrem Home-Verzeichnis im langen Format: joe@debian:~$ ls -la ~/ Das Zusammenziehen von Optionen ist nicht bei allen Kommandos erlaubt, siehe Manual. 2.8.9 Arbeiten mit Verzeichnissen Home-Verzeichnis, Arbeitsverzeichnis Zusammengehörende Daten – beispielsweise der Text eines Briefes – werden in einer Datei abgelegt. Zusammengehörende Dateien – beispielsweise alle Briefe an einen
140
2 Linux/UNIX
Empfänger – werden in ein Verzeichnis eingeordnet. Verzeichnisse können ihrerseits wieder in übergeordneten Verzeichnissen zusammengefasst werden, sodass sich eine Baumstruktur (E: tree, F: arborescence) ergibt, deren Knoten (E: vertex, F: nœud, sommet) die Verzeichnisse und deren Blätter die Dateien sind. Wurzel oben14 . Eine zweckmäßige Verzeichnisstruktur ist für die Arbeit ebenso wichtig wie Ordnung in papiernen Unterlagen. Während die Systemverzeichnisse weitgehend durch Standards und Konventionen vorgegeben sind und nur vom Verwalter geändert werden könnten (der sich hüten wird, das zu tun), ist ein Benutzer für die Verzeichnisstruktur unterhalb seines Home-Verzeichnisses selbst zuständig. Iin unserem zweiten DebianBuch ist ein eigener Abschnitt der Verzeichnisstruktur eines Textprojektes gewidmet, die wesentlich ist für ein effektives Schreiben und Formatieren. Die Zugriffsrechte der Verzeichnisse tragen zur Datensicherheit bei. Jedem Benutzer ist ein Home-Verzeichnis zugeordnet, und zwar durch einen Eintrag in der Datei /etc/passwd. Das ist das Verzeichnis, in dem der Benutzer selbst Dateien und Unterverzeichnisse anlegen und verändern darf. Wir haben den Begriff schon einige Male gebraucht. Nach der Anmeldung am System ist das Home-Verzeichnis des Benutzers das Arbeitsverzeichnis seiner Shell. Man befindet sich nach der Anmeldung immer dort, wo man auch alle Rechte an den Dateien hat. Außerhalb seines Home-Verzeichnisses hat ein Benutzer nur eng begrenzte Rechte, in fremden Home-Verzeichnissen normalerweise keine. Darüber hinaus ist jedem Programm – auch der Bash – ein Arbeitsverzeichnis (aktuelles V., E: working directory, current directory, F: répertoire courant, répertoire de travail) zugeordnet. Das Arbeitsverzeichnis bestimmt, wo Dateien gesucht oder neu angelegt werden. Immer wenn nicht ausdrücklich etwas anderes verlangt wird, werden Dateien in dem jeweiligen Arbeitsverzeichnis angelegt oder gesucht. Das Arbeitsverzeichnis kann stets – unabhängig von seinem Namen – mit einem einzelnen Punkt bezeichnet werden. Anzeigen des Arbeitsverzeichnisses Um Ihr Arbeitsverzeichnis anzuzeigen, geben Sie: joe@debian:~$ pwd (print working directory) ein. Sie erhalten eine Ausgabe wie: /home/joe oder /home/joe/texte wobei anstelle von joe Ihr Benutzername steht, vorausgesetzt bei der Namensgebung wurde der Konvention gefolgt, die Home-Verzeichnisse mit den Benutzernamen zu kennzeichnen, was nicht zwingend ist. Danach erscheint wieder der Prompt, und Sie können ein neues Kommando eingeben. 14
Baum: Wurzel unten = Wald, Wurzel oben = Informatik, keine Wurzel = Segelschiff
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
141
An der Ausgabe /home/joe ist zu erkennen, dass das Verzeichnis joe ein Unterverzeichnis des Verzeichnisses home ist. Verzeichnisnamen werden unter Linux/UNIX durch einen Schrägstrich getrennt. Hieraus folgt, dass ein Schrägstrich niemals Teil eines Datei- oder Verzeichnisnamens sein darf, wie bereits bemerkt. Dies ist für viele Linux-Anfänger ungewohnt, weil andere Betriebssysteme Verzeichnisnamen durch einen umgekehrten Schrägstrich trennen. Gemäß Konvention werden die Verzeichnisse der gewöhnlichen Benutzer unterhalb des Verzeichnisses /home angelegt. Gäbe es auf Ihrem System zwei Benutzer mit den Namen joe und eva, so würde das Verzeichnis /home die beiden Unterverzeichnisse joe und eva beinhalten. Während das Verzeichnis /home dem Benutzer root gehört und die Zugriffsrechte 755 aufweist – sodass jeder Benutzer hineinwechseln und lesen, aber nicht Unterverzeichnisse anlegen oder löschen darf – sollen die einzelnen Home-Verzeichnisse den jeweiligen Benutzern gehören und durch restriktive Zugriffsrechte (700) geschützt sein. Nur Root hat sein HomeVerzeichnis direkt unterhalb der Wurzel des Verzeichnisbaums. Jetzt ist es höchste Zeit, sich mit dem online-Handbuch (Manual, man-Seiten) vertraut zu machen. Zu fast allen Kommandos gibt es eine als Datei abgespeicherte, einheitlich aufgebaute Beschreibung. Geben Sie joe@debian:~$ man pwd ein, um die Beschreibung zu pwd auf den Bildschirm zu bekommen. pwd ist ein einfaches Kommando ohne Optionen oder Argumente, entsprechend kurz fällt die Beschreibung aus. Die Beschreibung des Manuals selbst erhalten Sie mittels joe@debian:~$ man man Sie sollten sich angewöhnen, sich auf diese Weise über für Sie neue Kommandos zu unterrichten. Ausführlich wird das Manual im Abschnitt 5.2 Manual auf Seite 274 erläutert. Erzeugen von Verzeichnissen Auf die Dauer wird es unübersichtlich, wenn Sie alle Ihre Dateien in Ihrem HomeVerzeichnis aufbewahren. Vielleicht wollen Sie Ihr Debian-System in erster Linie dazu verwenden, Briefe zu schreiben und Bilddateien zu erstellen und zu bearbeiten. Es wäre sinnvoll, für diese beiden Gebiete je ein Unterverzeichnis anzulegen. Das geht mit dem Kommando: joe@debian:~$ mkdir grafik briefe mkdir braucht mindestens ein Argument, übernimmt aber auch mehrere. Eine Regel besagt, dass man ein Verzeichnis spätestens dann unterteilen soll, wenn es mehr als einige hundert Einträge enthält. Anzeigen von Verzeichnissen und Dateien Das Kommando ls (list) dient dem Anzeigen oder Auflisten von Verzeichnissen. Geben Sie folgendes Kommando ein:
142
2 Linux/UNIX
joe@debian:~$ ls ls hat viele Optionen – siehe man ls – von denen aber nur wenige oft gebraucht werden. Die wichtigste ist -l (long), die ausführliche Angaben über die Dateien und Verzeichnisse verlangt. Wechseln des Arbeitsverzeichnisses Angenommen, Sie wollten einen Brief schreiben. Der soll im Verzeichnis briefe entstehen. Sie müssen dieses Verzeichnis zu Ihrem Arbeitsverzeichnis machen. Dazu dient das Kommando cd (change directory, ein in die Shell eingebautes Kommando). Auch dieses Kommando benötigt ein Argument, nämlich den Namen des Verzeichnisses, in das gewechselt werden soll. Geben Sie das Kommando joe@debian:~$ cd briefe ein, um in das Verzeichnis briefe zu wechseln. Um zurück in Ihr HomeVerzeichnis zu gelangen, gibt es folgende Wege: •
• •
Mit dem Kommando cd .. gelangen Sie in das Verzeichnis über dem Verzeichnis, in dem Sie sich gerade befinden. Zwischen cd und .. muss ein Leerzeichen stehen. Die Zeichenkette .. kennzeichnet immer das nächsthöhere Verzeichnis. Auch die Kombination ../.. ist erlaubt. Mit dem Kommando cd - (Minuszeichen) gelangen Sie in das Verzeichnis, in dem Sie sich befanden, bevor Sie das letzte cd-Kommando eingegeben haben. Mit dem Kommando cd ohne Argument gelangen Sie immer in Ihr HomeVerzeichnis, egal wo Sie sich vorher befanden.
Zur Übung können Sie in das Verzeichnis grafik wechseln und dann zurück in Ihr Home-Verzeichnis. Überprüfen Sie dabei jedes Mal mit pwd, ob Sie im richtigen Verzeichnis gelandet sind. Der Aufruf man cd funktioniert nicht, da cd ein internes Kommando der Shell ist. Sie finden seine Beschreibung mittels man bash im Abschnitt Shell Builtin Commands der Manualseite. Löschen von Verzeichnissen Möchten Sie eines der eben angelegten Verzeichnisse wieder löschen, dient hierzu das Kommando rmdir (remove directory), sofern das Verzeichniss leer ist. Um das Verzeichnis grafik zu löschen, ist das Kommando joe@debian:~$ rmdir grafik einzugeben. Voraussetzung ist, dass Sie sich im Home-Verzeichnis befinden, weil das Verzeichnis grafik dort liegt und von einem anderen Verzeichnis aus vom Kommando nicht gefunden wird. Löschen Sie das Verzeichnis grafik und erzeugen Sie es erneut. Wechseln Sie dann in dieses Verzeichnis. Falls ein Verzeichnis nicht leer ist, löschen Sie es samt allen Unterverzeichnissen und Dateien mittels:
2.8 Dateien und Verzeichnisse – aus der Sicht des Benutzers
143
joe@debian:~$ rm -rf verzeichnisname aber Vorsicht, das Kommando ist gnadenlos und löscht ohne Rückfragen das Verzeichnis mit allem, was darin ist. Die vorstehend erläuterten Kommandos zum Umgang mit Dateien und Verzeichnissen gehören zu den Dienstprogrammen (E: utility, F: utilitaire), kleine Helfer, die man auf jedem Rechner braucht, um die eigentliche Arbeit erledigen zu können. Im Zeitalter grafischer Arbeitsumgebungen werden sie unter der Oberfläche eines DateiManagers wie konqueror (KDE) oder nautilus (GNOME) zusammengefasst, siehe Abschnitt 3.8 Datei-Manager auf Seite 216. 2.8.10 Ändern von Systemdateien und -verzeichnissen Das Kommando su Es wurde bereits darauf hingewiesen, dass ein gewöhnlicher Benutzer keine Teile des Betriebssystems verändern kann. Falls Sie zugleich Ihr eigener Systemverwalter sind, werden Sie dies gelegentlich tun. Sie könnten sich dazu abmelden und als Root wieder anmelden. Danach haben Sie alle notwendigen Rechte. Dieses Vorgehen ist unbequem, gerade wenn Sie bereits eine Reihe von Programmen aufgerufen haben, die Sie erst beenden und später wieder neu starten müssten. Deswegen kann zum Wechsel der Identität auch das Kommando su (substitute) verwendet werden. Wird su ohne Argument aufgerufen: joe@debian:~$ su so wird die Benutzeridentität auf Root gesetzt. Es ist auch möglich, als Argument den Namen eines gewöhnlichen Benutzers anzugeben, um kurzzeitig dessen Identität anzunehmen, was seltener vorkommt. Ändern der Datei /etc/motd Vielleicht möchten Sie nach der Anmeldung an Ihrem System etwas freundlicher begrüßt werden? Die Datei motd (message of the day) enthält den Text, der nach der Anmeldung ausgegeben wird. Sie befindet sich wie viele Konfigurationsdateien im Verzeichnis /etc. Zur Verwaltung eines Debian-GNU/Linux-Systems müssen Sie deswegen vorwiegend Dateien in diesem Verzeichnis oder seinen Unterverzeichnissen ändern. Geben Sie folgendes Kommando ein, um aus Ihrer Sitzung heraus Systemverwalter zu werden: joe@debian:~/texte$ su Sie werden nun nach einem Passwort gefragt. Geben Sie das Verwalterpasswort ein. Wenn Sie sich vertippen, schlägt su fehl, und Sie erhalten die Fehlermeldung: su: Authentication failure
144
2 Linux/UNIX
su teilt Ihnen mit, dass Ihre Anmeldung falsch war. Sie können es dann erneut versuchen, indem Sie nochmals su eingeben. Wenn Sie das richtige Passwort eingegeben haben, sehen Sie jetzt den Prompt des Systemverwalters. Nun haben Sie volle Verwalterrechte samt -verantwortung. Wenn Sie folgendes Kommando eingeben: debian:/home/joe/texte# vi /etc/motd starten Sie den Editor vi und öffnen die Datei /etc/motd. Wie Sie sehen, wurde ein absoluter Pfadname benutzt. Sie können nun eine neue Zeile mit dem Text Herzlich willkommen! anfügen und die Datei abspeichern. Um nach getaner Arbeit die Root-Rechte wieder aufzugeben und als gewöhnlicher Benutzer weiterzuarbeiten, geben Sie folgendes Kommando ein: debian:/home/joe/texte# exit Damit wird die Shell des Verwalters beendet, und Sie befinden sich wieder in der Sitzungs-Shell, die Sie durch Ihre Anmeldung als gewöhnlicher Benutzer gestartet haben. Mit dem Kommando whoami (wer bin ich) oder id (identity) lassen Sie sich jederzeit Ihre Identität anzeigen. Probieren Sie es als Benutzer und als Systemverwalter aus. Identitätswechsel werden in der Datei /var/log/auth.log protokolliert. In Abschnitt 12.5.3 Benutzern Verwalterrechte übertragen auf Seite 530 lernen wir weitere Wege zum Übertragen von Verwalterrechten kennen.
2.9 Datenträger 2.9.1 Grundbegriffe Datenträger sind Hardware, auf der große Datenmengen dauerhaft gespeichert werden, mit anderen Worten Speichermedien oder Massenspeicher (E: mass storage, F: stockage de masse). Arbeitsspeicher oder Zwischenspeicher (Cache) zählen nicht dazu. Externe oder entfernbare Datenträger (E: removable media, F: moyen amovible) sind Speichermedien, die aus einem Rechner herausgenommen und zu einem anderen Rechner transportiert oder als Sicherungskopien im Tresor aufbewahrt werden. Dazu zählen: • • • • • • • • •
Festplatten (E: hard disk, F: disque dur), die fest in den Rechner eingebaut sein können, aber auch herausnehmbar oder als externe Platte angeschlossen, Disketten (Schlappscheiben, E: floppy disc, F: disquette) verschiedener Durchmesser von 3 bis 8 Zoll, ZIP- und JAZ-Disketten, Magnetbänder (E: tape, F: bande magnétique), magnetooptische Platten (MO), USB-Stöpsel (Speicherstift, E: USB stick, USB flash drive, F: clé USB), Speicherkarten (Compact Flash, Smart Media, Secure Digital etc., E: memory card, F: carte mémoire), Compact Discs (CD, CDROM), Digital Versatile Discs (DVD).
2.9 Datenträger
145
Auf externe Medien kann auf zweierlei Art zugegriffen werden: • •
Sie enthalten ein von Linux/UNIX unterstütztes Dateisystem. Dann werden sie in das Wurzeldateisystem eingehängt, wie nachstehend beschrieben. Sie enthalten ein Archiv (tar, cpio). Dann schreibt oder liest das Kommando von oder zu der zugehörigen Gerätedatei wie /dev/cdrom.
Darüber hinaus verfügt Linux über besondere Werkzeuge (mtools) zum Arbeiten mit Disketten, auf denen ein DOS-Dateisystem eingerichtet ist. Im Laufe der Jahrzehnte sind – entsprechend den unterschiedlichen Möglichkeiten und Anforderungen der Speichermedien – mehrere Dutzend verschiedener Dateisystemtypen entwickelt worden. Übersichten und Einzelheiten findet man im Filesystems HOWTO (2007) von M ARTIN H INNER. In Abschnitt 13 Dateisysteme ab Seite 563 gehen wir auf Dateisystemtypen näher ein. 2.9.2 Gerätebezeichnungen Falls Sie Betriebssysteme wie DOS oder MS Windows kennen, werden Sie sich vielleicht darüber wundern, dass bei der Angabe von Pfadnamen nie ein Laufwerksbuchstabe benutzt wird. Laufwerksbuchstaben existieren unter Linux/UNIX nicht. Es gibt beim Umgang mit Dateien keine Laufwerke (E: drive, F: lecteur, dérouleur), sondern nur ein einziges großes Dateisystem, in das alle verfügbaren Festplatten, Disketten, CD/DVDs, Stöpsel oder sonstigen Medien eingebunden werden. Trotzdem haben die Geräte oder Laufwerke Namen, die jedoch anders gebraucht werden als unter DOS oder MS Windows. Geräte wie Festplatten oder Diskettenlaufwerke werden durch spezielle Dateien dargestellt, die sich im Verzeichnis /dev befinden. Diese Dateien werden Gerätedateien genannt. Wir haben kurz in Abschnitt 2.8.4 Gerätedateien auf Seite 127 über sie gesprochen. Einige Gerätenamen für Festplatten oder CD/DVD-Laufwerke lernen Sie während der Einrichtung kennen. Es existiert jedoch eine Anzahl weiterer Gerätedateien, beispielsweise auch solche, die Bandlaufwerke (Streamer) oder den Sound-Mixer auf einer Soundkarte darstellen. Die Gerätedateien für Diskettenlaufwerke heißen fd0 (für Floppy-Disk 0), fd1, fd2 usw. Dabei entspricht /dev/fd0 dem Diskettenlaufwerk, das Sie unter DOS über den Laufwerksbuchstaben A: ansprechen, /dev/fd1 dem zweiten Laufwerk (unter DOS: B:) usw. Entsprechendes gilt für andere Datenträger. Eine Übersicht über die gebräuchlichen Gerätedateien findet sich im Anhang, Tabelle 23 Gerätedateien auf Seite 1065. Der gewöhnliche Benutzer braucht sich um die Gerätedateien nicht zu kümmern, der Verwalter bekommt mit ihnen zu tun. Vieles im Verzeichnis /dev läuft heute automatisch ab. 2.9.3 Einhängen von Datenträgern (Mounten) Von Linux/UNIX unterstützte Dateisysteme auf beliebigen internen oder externen Datenträgern werden durch Einhängen (mounten, einbinden, montieren, E: to mount, F: monter) zu einem Zweig des eigenen Wurzel- oder Rootdateisystems. Einhängen heißt, ein Verzeichnis – meist die Wurzel – des hinzukommenden Dateisystems
146
2 Linux/UNIX
auf ein Verzeichnis des eigenen Dateisystems abzubilden. Dieses Verzeichnis nennt man Einhängepunkt (E: mounting point, F: point de montage); es sollte keine Einträge enthalten. Ist dies nicht der Fall, sind die Einträge nicht sichtbar, solange das hinzugekommene Dateisystem eingehängt ist. Sie sind jedoch nicht verloren und tauchen wieder auf. Einige Einhängepunkte wie das Verzeichnis /floppy oder /media/floppy, das zum Einhängen von Disketten gedacht ist, werden während der Grundeinrichtung angelegt. Es ist möglich, ein Dateisystem gleichzeitig in mehrere Einhängepunkte desselben Wurzeldateisystems einzuhängen. Ebenso dürfen mehrere Dateisysteme gleichzeitig in verschiedene Einhängpunkte eingehängt sein, nur ist darauf zu achten, dass keine geschlossenen Wege (Schleifen) im Dateisystem erzeugt werden. Es kann auch zu Problemen führen, wenn ein Verzeichnis und dann noch einmal ein Unterverzeichnis davon in dasselbe Wurzelsystem eingehängt werden. Also nicht kreuz und quer einhängen! Ein nicht mehr benötigtes Dateisystem lässt sich aushängen. Solange es noch in Gebrauch ist, verweigert das System das Aushängen. Beim Herunterfahren des Systems werden alle Dateisysteme automatisch ausgehängt. Normalerweise ist der direkte Zugriff auf Hardwarekomponenten nur dem Verwalter gestattet. Weil das Einhängen von Datenträgern einen Hardwarezugriff darstellt, kann dieser Vorgang nur vom Verwalter durchgeführt werden. Er hat die Möglichkeit zu erlauben, dass bestimmte Datenträger auch von gewöhnlichen Benutzern eingehängt werden. Entscheidend ist ein Eintrag (Option user) in der Datei /etc/fstab. Einzelheiten zum mount-Kommando erfährt man mittels man mount. Ohne Optionen oder Argumente von einem beliebigen Benutzer aufgerufen zeigt mount eine Liste der augenblicklich eingehängten Dateisysteme an. Dem Kommando mount sind normalerweise zwei Argumente zu übergeben, zum einen die Gerätedatei, unter der das externe Dateisystem liegt, und zum anderen der Name des Verzeichnisses, in das das externe Dateisystem eingebunden werden soll (Einhängepunkt). Die Syntax des Kommandos lautet in einfachen Fällen: mount Gerätedatei Einhängepunkt Das mag zunächst umständlich erscheinen. Es ist jedoch nur durch die Anmeldung eines Datenträgers beim Betriebssystem möglich, den Inhalt des Datenträgers zwischenzuspeichern (zu puffern). Dadurch können Schreib- und Lesevorgänge aus Sicht des Benutzers eher beendet werden, weil gegebenenfalls nur aus dem Puffer gelesen beziehungsweise in ihn geschrieben wird. Das wirkliche Schreiben findet erst statt, wenn das Betriebssystem Zeit dazu hat oder der Datenträger abgemeldet wird. Würde das Betriebssystem sich nicht darauf verlassen können, dass der Datenträger noch vorhanden ist, müsste es bei jeder Leseoperation nachschauen und Schreiboperationen sofort durchführen, weil der Datenträger ja im nächsten Moment vom Benutzer entfernt werden könnte. Außerdem ist es möglich, dass andere Benutzer über das Netz ebenfalls auf den Datenträger zugreifen. Hier würde ein Fehlerzustand auftreten, wenn ein Benutzer gerade auf eine Diskette schreibt und ein anderer sie gleichzeitig aus dem Laufwerk nimmt. Die Folge wäre eine zerstörte Datei, im schlimmsten Fall sogar ein zerstörtes Dateisystem auf der Diskette. Aus diesem Grunde sperren die meisten Laufwerke
2.9 Datenträger
147
für Wechseldatenträger die Ausgabetaste, solange der Datenträger eingehängt ist. Diskettenlaufwerke bieten diese Möglichkeit bei PCs leider nicht, sodass man sich hier mit der vertrauensvollen An- und Abmeldung begnügen muss. Wem das Ein- und Aushängen von Wechseldatenträgern auf Dauer zu mühsam ist, kann zum einen mit den mtools direkt auf DOS-formatierte Datenträger zugreifen, wobei auf alle Vorteile des Einhängens in das Dateisystem verzichtet wird. Zum anderen besteht die Möglichkeit, einen Automounter einzusetzen, der Datenträger in dem Augenblick einhängt, in dem auf sie zugegriffen wird, und aushängt, wenn eine Zeit lang kein Zugriff mehr erfolgt ist (siehe Abschnitt 2.9.8, Der Automounter im Kern auf Seite 153). Darüber hinaus wird das Einhängen von Datenträgern bei der Arbeit mit grafischen Benutzeroberflächen vereinfacht, weil der Vorgang hier mit der Maus gesteuert wird. Um eine Diskette einzuhängen, schieben Sie die Diskette in das DiskettenLaufwerk und geben als Verwalter folgendes Kommando ein: debian:~# mount /dev/fd0 /media/floppy In der Regel leuchtet dann die Lampe des Diskettenlaufwerks kurz auf und sein Motor läuft an. Wenn Sie keine Fehlermeldung erhalten, ist der Datenträger nun mit dem Verzeichnis /media/floppy verbunden. Falls ein Fehler auftritt, sollten Sie folgende Punkte überprüfen: • •
• •
Ist /dev/fd0 die richtige Gerätedatei? Versuchen Sie es auch mit /dev/fd1. Ist die Diskette mit einem Dateisystem formatiert, dass vom Linux-Kern unterstützt wird? Vom Standardkern werden u. a. DOS/MS Windows-, ext2- (Linux) und Minix-formatierte Datenträger erkannt und unterstützt. Ist die Unterstützung für Diskettenlaufwerke in Ihren Kern eincompiliert? Das ist beim Standardkern der Fall. Sind Sie als Root angemeldet oder haben Sie wenigstens Ihre Identität mit su zur Identität des Verwalters gewechselt?
Den Inhalt der Diskette können Sie sich bei Erfolg wie den Inhalt jedes anderen Verzeichnisses anzeigen lassen. Geben Sie folgendes Kommando ein: debian:~# ls -l /media/floppy Sie sollten jetzt die Dateien auf der Diskette aufgelistet bekommen. Wenn die Diskette leer ist, sehen Sie natürlich keine Dateien. Probieren Sie es dann nochmal mit einer anderen Diskette. Vorher müssen Sie die momentan eingehängte Diskette jedoch wieder aus dem Dateisystem aushängen. Für CDs oder DVDs gilt dasselbe, nur dass das Dateisystem ein anderes ist. Auf CDs findet man überwiegend das Dateisystem nach ISO 9660 (ECMA 119/2), während auf DVDs zunehmend das Universal Disc Format (UDF) verwendet wird. Beide werden von aktuellen Linux-Systemen beherrscht. Weiteres dazu in Abschnitt 13.6 Dateisysteme für entfernbare Medien auf Seite 580.
148
2 Linux/UNIX
2.9.4 Aushängen von Datenträgern (Unmounten) Sie dürfen eine Diskette oder CD/DVD nie aus dem Laufwerk nehmen, bevor Sie sie ordnungsgemäß aus dem Dateisystem ausgehängt haben. Andernfalls drohen Datenverluste. Zum Aushängen (unmounten, demontieren, trennen, E: to unmount, F: démonter) eines Datenträgers aus dem Rootdateisystem dient das Kommando umount. Sie können umount als Argument entweder die Gerätedatei des eingehängten Datenträgers oder das Verzeichnis, in das sie diesen eingehängt haben (Einhängepunkt), übergeben. Die Kommandos: debian:~# umount /media/floppy debian:~# umount /dev/fd0 bewirken dasselbe: Der vorher in das Verzeichnis /media/floppy eingehängte Datenträger /dev/fd0 wird aus dem Rootdateisystem ausgehängt. Erst danach dürfen Sie den Datenträger (Diskette oder CD/DVD) aus dem Laufwerk nehmen. Das Kommando eject erledigt Aushängen und Auswerfen in einem Zug. Falls auf dem eingehängten Datenträger noch eine Datei geöffnet ist, wird das Aushängen verweigert mit der Meldung device is busy. Dann geht die Suche nach der offenen Datei los. Der Aufruf: debian:~# lsof /dev/cdrom mit der Gerätedatei des auszuhängenden Mediums als Argument listet alle offenen Dateien dieses Mediums auf. Falls die Auskunft noch nicht die nötige Klarheit bringt, versucht man als nächstes, mittels ps oder fuser einen Besitzer zu dem Prozess oder der Datei herauszufinden: debian:~# fuser -vm /media/stick Als Antwort erhalten Sie Prozess-IDs. Den zugehörigen Prozessnamen samt Benutzer nennt Ihnen: joe@debian:~$ ps -ef | grep PID Die Benutzung eines Verzeichnisses als Arbeitsverzeichnis gilt als Öffnen des Verzeichnisses, verhindert also auch das Aushängen. Die meisten Texteditoren arbeiten auf einer Arbeitskopie der Textdatei und öffnen diese nur zum anfänglichen Lesen und zum Zurückschreiben. Diese Art der Benutzung lässt sich nicht feststellen. Sie stört beim Aushängen aber auch nicht, nur der Schreiber wird sich wundern, wenn er zurückschreibt. Voraussichtlich landet die Frucht seiner Mühen im Einhängepunkt und ist damit beim nächsten Einhängen unsichtbar. Wer mit einer grafischen Benutzeroberfläche und mehreren Arbeitsflächen arbeitet, übersieht leicht ein Fenster, das noch mit dem Datenträger zu tun hat. Auf das Ein- und Aushängen kommen wir in Abschnitt 13.10 Verwalten des Dateisystems auf Seite 620 ausführlicher zu sprechen.
2.9 Datenträger
149
2.9.5 Kopieren von Dateien auf externe Datenträger Nachdem ein Datenträger eingehängt ist, wird sein Dateisystem wie ein normales Verzeichnis benutzt. Sie verwenden dieselben Kommandos, die Sie schon kennen gelernt haben, um Dateien zu kopieren, zu verschieben oder zu löschen. Wir wollen nun die Datei GPL-Beer aus dem Unterverzeichnis texte Ihres Home-Verzeichnisses auf eine Diskette kopieren. Sie hängen die Diskette ein, wenn sie nicht schon eingehängt ist, wechseln in das Verzeichnis texte und kopieren die Datei mit dem Kommando: debian:/home/joe/texte# cp GPL-Beer /media/floppy in das Verzeichnis /media/floppy, also auf die Diskette. Lassen Sie sich zur Kontrolle anzeigen, ob die Datei auf der Diskette angekommen ist. Wechseln Sie dazu mit cd in das Verzeichnis /media/floppy und geben dort das Kommando ls -l ein. In umgekehrter Richtung funktioniert das Kopieren genauso. Natürlich können Sie Dateien auf einem externen Datenträger auch direkt bearbeiten, sobald er eingehängt ist. Hängen Sie die Diskette mit der Datei GPL-Beer ein und verändern Sie die darauf befindliche Datei mit einem Editor Ihrer Wahl. Danach kopieren Sie zur Übung eine Datei von einer Diskette auf eine andere Diskette. Wenn Sie nur ein Diskettenlaufwerk haben, müssen Sie die Datei zunächst in Ihr Home-Verzeichnis kopieren, die erste Diskette aushängen, dann die zweite Diskette einlegen und -hängen und schließlich die Datei von Ihrem Home-Verzeichnis auf die zweite Diskette kopieren. Die zum Zwischenspeichern benötigte Kopie in Ihrem Home-Verzeichnis löschen Sie hernach. 2.9.6 Die Dateisystemtabelle (fstab) Aufbau In der Systemkonfigurationsdatei /etc/fstab (file system table) wird festgelegt, welche Datenträger beim Systemstart automatisch in welche Verzeichnisse eingehängt werden sollen. Darüber hinaus wird das Einhängen durch gewöhnliche Benutzer vorbereitet. Diese Datei ist wichtig, weil während des Systemstarts zumindest der Datenträger feststehen muss, auf dem sich das Wurzeldateisystem befindet. Eine fehlende oder fehlerhafte fstab hat ernsthafte Probleme beim Systemstart zur Folge, die möglicherweise einen Start von einem entfernbaren Datenträger (Live-CD) erfordern. Sehen Sie sich die Datei mit cat oder less an. Sie könnte Folgendes beinhalten: # /etc/fstab: static file system information. # #<mnt pt> <pass> /dev/hda6 / ext2 defaults 0 1 /dev/hda5 none swap sw 0 0 proc /proc proc defaults 0 0
150
2 Linux/UNIX
Unterhalb der hier wiedergegebenen Einträge befinden sich weitere, wenn Sie bereits während der Grundeinrichtung zusätzliche Datenträger eingebunden haben. Die Datei ist folgendermaßen aufgebaut: •
• • • • •
• •
Leere Zeilen und solche, die mit einem Doppelkreuz beginnen, haben keine Bedeutung und dienen dem übersichtlichen Aufbau sowie der Kommentierung der Datei (wie bei der Bash). Jede andere Zeile bezeichnet die Zuordnung eines Datenträgers zu einem Verzeichnis. Diese Zeilen bestehen aus sechs verschiedenen Feldern (Spalten). Dabei steht in der ersten Spalte die Bezeichnung der Gerätedatei, die den einzuhängenden Datenträger darstellt. In der zweiten Spalte steht der Name des Verzeichnisses, in das der Datenträger eingehängt werden soll (Einhängepunkt). In der dritten Spalte steht der Typ des Dateisystems auf dem Datenträger, also das Format, in dem der Datenträger formatiert ist. In der vierten Spalte folgen Optionen, die teilweise vom Typ des Dateisystems auf dem Datenträger abhängen. Einzelne Optionen werden durch Kommata voneinander getrennt. Zwischen den Optionen dürfen sich keine Leerzeichen befinden, weil dann die Optionen hinter dem Leerzeichen als Inhalt der nächsten Spalte aufgefasst würden. In der fünften Spalte stehen Informationen für das Programm dump, die zur Zeit nicht beachtet werden. In der sechsten Spalte finden sich Informationen darüber, ob und wie die Datenträger beim Systemstart auf ihre Unversehrtheit geprüft werden sollen. Der Wert 0 bedeutet, dass der Datenträger nicht geprüft werden soll (sinnvoll bei CD/DVDs). Der Wert 1 bedeutet, dass der Datenträger vor allen anderen geprüft werden soll. Der Datenträger mit dem Rootdateisystem (/) erhält diesen Wert. Alle anderen Datenträger sollten anschließend geprüft werden und deswegen den Wert 2 erhalten.
Von den Optionen im Anschluss an defaults sind vor allem interessant: • • • •
ro readonly, beispielsweise bei /usr denkbar, nodev lässt keine Gerätedateien zu, bei fast allen Verzeichnissen sinnvoll, nosuid übergeht das Set-UID- und das Set-GID-Bit, beispielsweise bei /tmp und /home zu empfehlen, noexec verhindert das Ausführen von Dateien als Programme.
So bequem die uneingeschränkte Option defaults ist, man erleichtert Bösewichten damit das Leben. Bei vielen Dateisystemen kann man ohne die rechtmäßig benötigte Funktionalität einzuschränken weitere Optionen anfügen. In der oben wiedergegebenen Datei /etc/fstab finden sich zwei besondere Einträge, die auch auf Ihrem System vorhanden sein sollten. Dies ist einmal der Eintrag für den Swapbereich. Dieser dient zum Auslagern von Inhalten des Arbeitsspeichers auf die Festplatte und ist deswegen nicht in das Dateisystem eingebunden. In der zweiten Spalte (mount point), in der normalerweise das Verzeichnis steht, in
2.9 Datenträger
151
das die Partition eingehängt werden soll, steht hier der Eintrag none. Der Swapbereich enthält kein Dateisystem, also nichts zum Einhängen. Die zweite Besonderheit ist das /proc-Dateisystem. Dieses Dateisystem stellt eine Schnittstelle zum Linux-Kern dar, über die Informationen aus dem Kern gelesen und an diesen übergeben werden können. Das proc-Dateisystem ist keinem physikalischen Datenträger zugeordnet, weswegen sich hier in der ersten Spalte, der Eintrag proc ohne Schrägstrich befindet. Sie können die Benutzung des /procDateisystems ausprobieren, indem Sie das Kommando joe@debian:~$ cat /proc/interrupts eingeben. In der Datei stehen die vom Kern benutzten Interrupts. Die swap- und proc-Dateisysteme werden beim Systemstart nicht geprüft, weil auf ihnen keine Daten gespeichert werden, die nach einem Start zur Verfügung stehen müssten. Anlegen eines Eintrags für das Diskettenlaufwerk Bevor Sie die Datei /etc/fstab ändern, sollten Sie eine Sicherheitskopie anlegen. Kopieren Sie die Datei dazu mit cp in Ihr Home-Verzeichnis. Da sie kurz ist, wäre auch an einen Ausdruck zu denken. Sie zählt zu den wertvollsten Dateien. Nun editieren Sie die Datei mit dem Editor vi. Dazu brauchen Sie Verwalterrechte, weil es sich bei der Datei um eine Systemkonfigurationsdatei handelt. Fügen Sie folgende Zeile hinzu: /dev/fd0 /media/floppy msdos defaults,user,noauto 0 0 Damit geben Sie in der ersten Spalte den Namen der Gerätedatei Ihres Diskettenlaufwerks an. Wenn Ihr Diskettenlaufwerk nicht /dev/fd0 ist, müssen Sie einen anderen Gerätedateinamen verwenden. In der zweiten Spalte geben Sie als Einhängepunkt das Verzeichnis /media/floppy an. Hierhin sollen Disketten in Zukunft per Voreinstellung eingehängt werden. Die dritte Spalte bestimmt das Dateisystem. Weil Disketten in der Regel DOS/MS-Windows-formatiert sind, geben Sie hier msdos oder vfat an. Die Variante vfat beinhaltet die Unterstützung für lange Dateinamen auf DOS-formatierten Disketten. In der Spalte mit den Optionen sind einige zusätzliche Angaben zu machen. Zunächst bestimmt defaults, dass die Vorgaben übernommen werden sollen. Welche das sind, finden Sie auf der Manualseite zu mount. Der Eintrag user legt fest, dass auch gewöhnliche Benutzer berechtigt sind, das Dateisystem einzuhängen. Der Eintrag noauto besagt, dass dieses Dateisystem nicht automatisch während des Systemstarts eingehängt werden soll. Das würde bei einem Diskettenlaufwerk keinen Sinn ergeben, weil sich nicht immer eine Diskette im Laufwerk befindet. In den letzten beiden Feldern sind übliche Werte eingetragen. Andere, häufig vorkommende Zeilen in der fstab sind: /dev/scd0 /media/cdrom0 iso9660 ro,user,noauto 0 0 /dev/sdc1 /media/stick0 msdos user,umask=0,noauto 0 0 server1:/var/www /var/www nfs defaults 0 0
152
2 Linux/UNIX
Die erste Zeile erlaubt gewöhnlichen Benutzern, eine CD/DVD in einem Laufwerk mit SCSI-Schnittstelle einzuhängen, und zwar nur zum Lesen (ro = readonly). Der zweite Eintrag gilt einem USB-Stöpsel. Der dritte schließlich hängt ein per NFS über das Netz von einem Server zur Verfügung gestelltes (exportiertes) Verzeichnis in die lokale Verzeichnisstruktur ein. Da lokal kein Webserver (HTTP-Dämon) läuft, mithin das lokale Verzeichnis /var/www unbelegt ist, konnte hier der gleiche Pfad wie auf dem Server gewählt werden. Das ist nicht zwingend. Ein umfangreicheres Beispiel lernen wir in Abschnitt 13.10.1 Anzeigen eingehängter Partitionen auf Seite 620 kennen. Wenn Sie die Datei /etc/fstab angepasst und gesichert haben, verlassen Sie den Editor und geben die Verwalterrechte auf. 2.9.7 Datenträger einhängen als Benutzer Nun können gewöhnliche Benutzer Disketten im Laufwerk /dev/fd0 ein- und aushängen. Hierbei braucht nicht mehr die Gerätedatei angegeben zu werden, die ja bereits in /etc/fstab genannt ist. Vielmehr reicht es aus, einfach das Verzeichnis als Parameter anzugeben, in das der vordefinierte Datenträger eingehängt werden soll. Zum Einhängen reicht das Kommando: joe@debian:~$ mount /media/floppy Gleichwertig ist die Angabe von /dev/fd0 an Stelle von /media/floppy. Und zum Aushängen: joe@debian:~$ umount /media/floppy Das gleiche bequeme Vorgehen, das Sie für die Verwendung von Disketten eingerichtet haben, möchten Sie vielleicht auch für CD/DVDs nutzen. Wenn sich in Ihrem Rechner ein CD/DVD-Laufwerk befindet, können Sie nun einen ähnlichen Eintrag für dieses Laufwerk anlegen. Es wird hier davon ausgegangen, dass Sie die Grundeinrichtung – zumindest teilweise – von CD/DVD durchgeführt haben und deswegen wissen, wie Ihre CD/DVD angesprochen werden kann. Sie benötigen folgende zusätzliche Informationen: •
•
•
Der Dateisystemtyp von CDs ist meist iso9660. Auf DVDs wird iso9660 oder udf verwendet. Näheres siehe man fstab. Alle Dateisysteme, die Ihr aktueller Kern unterstützt, sind in /proc/filesystems aufgelistet. Analog zum Verzeichnis /media/floppy, in das Sie Disketten einhängen, wird während der Grundeinrichtung das Verzeichnis /media/cdrom erzeugt, das zum Einhängen von CD/DVDs vorgesehen ist. Weil es eine Vielzahl verschiedener CD/DVD-Laufwerkstypen gibt, existiert eine große Anzahl von Gerätedateien, die diese darstellen. Während der Grundeinrichtung wurde die Datei /dev/cdrom angelegt, die ein Verweis (Symlink) auf die für Ihr System gültige CD/DVD-Gerätedatei ist. Beispielsweise kann /dev/cdrom ein Symlink auf /dev/scd0 sein. Verwenden Sie /dev/cdrom, um CD/DVDs einzuhängen.
2.9 Datenträger
•
153
Weil von CD/DVDs nur gelesen werden kann und sie nicht beschrieben werden können, gibt es normalerweise eine Warnung beim Einhängen dieser Datenträger. Verwenden Sie deswegen in der Datei /etc/fstab die Option ro (read-only), um die Warnung zu erübrigen.
Wenn Sie den entsprechenden Eintrag vorgenommen haben, prüfen Sie, ob Sie CD/DVDs als Benutzer einhängen können, und lassen Sie sich den Inhalt einer CD/DVD anzeigen. Lassen Sie sich auch den Inhalt von Unterverzeichnissen auf der Scheibe anzeigen. DVDs lassen sich nur in DVD-Laufwerken lesen, CDs sowhl in CD- wie in DVD-Laufwerken. 2.9.8 Der Automounter im Kern Der Linux-Kern verfügt über die Fähigkeit, Datenträger automatisch einzuhängen, sobald auf sie zugegriffen wird. Dazu muss der Kern mit der Option Kernel automounter support übersetzt worden sein, bei Standardkernen gegeben. Außerdem muss das Paket autofs eingerichtet sein. Es enthält die Programme und Dateien, die benötigt werden, um diese Eigenschaft nutzen zu können15 Über ein Programm werden dem Kern Verknüpfungen von Verzeichnissen und Datenträgern mitgeteilt. Sobald auf ein solches Verzeichnis zugegriffen wird, mountet der Kern den Datenträger automatisch, die Notwendigkeit zum manuellen Einhängen entfällt. Die beiden wichtigsten Anwendungsbereiche des Systems sind: •
•
Netzumgebungen, in denen von allen Rechnern aus auf eine große Anzahl von Verzeichnissen zugegriffen wird, die auf anderen Rechnern liegen. Dabei ist es unerwünscht, dass alle Verzeichnisse ständig eingehängt sind. Durch den Einsatz eines Automounters wird die Verbindung erst dann aufgebaut, wenn auf das Verzeichnis zugegriffen wird. Beim Zugriff auf auswechselbare Datenträger wie Disketten oder CD/DVDs wird es oft als lästig empfunden, dass diese manuell eingehängt werden müssen, bevor auf sie zugegriffen werden kann. Der Automounter nimmt dem Benutzer das manuelle Einhängen der Medien ab.
Das Paket autofs wird über die Datei /etc/auto.master konfiguriert. Sie sieht nach der erstmaligen Einrichtung des Paketes so aus: # $Id: extern.tex,v 1.13 2007/05/19 20:47:08 wulfa Exp $ # Sample auto.master file # Format of this file: # mountpoint map options # For details of the format look at autofs(8). /misc /etc/auto.misc --timeout=60 Leere Zeilen und solche, die mit einem Doppelkreuz beginnen, haben keine Bedeutung. Alle übrigen Zeilen müssen folgendes Format haben: 15
Ein anderes Automounting-System steht mit dem Paket am-utils zur Verfügung.
154
2 Linux/UNIX
Verzeichnis Konfigurationsdatei [Optionen] Mit Verzeichnis wird ein Verzeichnis angegeben, für das der Automounter zuständig sein soll. Für jedes Verzeichnis gibt es eine eigene Konfigurationsdatei, die beschreibt, welche Datenträger in welches Unterverzeichnis des angegebenen Verzeichnisses bei Bedarf eingehängt werden. In obiger Datei wird angegeben, dass der Automounter das Verzeichnis /misc kontrollieren soll und dass in der Datei /etc/auto.misc angegeben ist, welcher Datentr:ager in welches Unterverzeichnis von /misc eingebunden werden soll. Hinter der Angabe der Konfigurationsdatei lassen sich Optionen angegeben. Die wichtigste Option ist --timeout, die bestimmt, dass nach Ablauf einer Zeit ein Datenträger wieder aus dem Dateisystem entfernt wird, wenn er nicht erneut benutzt worden ist. Die Zeit ist hinter der Option in Sekunden anzugeben, der voreingestellte Wert beträgt 300 Sekunden. Die Konfigurationsdatei für das Verzeichnis /misc (/etc/auto.misc) ist in dem Paket ebenfalls enthalten. Sie sieht nach der erstmaligen Einrichtung so aus: # # # #
$Id: extern.tex,v 1.13 2007/05/19 20:47:08 wulfa Exp $ This is an automounter map having the following format key [ -mount-options-separated-by-comma ] location Details may be found in the autofs(5) manpage
kernel boot removable cd floppy
-ro -fstype=ext2 -fstype=ext2 -fstype=iso9660,ro -fstype=auto
ftp.kernel.org:/pub/linux :/dev/hda1 :/dev/hdd :/dev/hdc :/dev/fd0
Auch in dieser Datei haben leere Zeilen und solche, die mit einem Doppelkreuz beginnen, keine Bedeutung. Alle anderen Zeilen bestehen aus drei Feldern, durch Leerzeichen voneinander getrennt. Die Felder bedeuten Folgendes: •
• •
Im ersten Feld steht der Name eines Unterverzeichnisses des Verzeichnisses, für das die Datei zuständig ist. Der erste Eintrag (kernel) bezeichnet in diesem Fall das Verzeichnis /misc/kernel und der zweite das Verzeichnis /misc/ boot. Im zweiten Feld befinden sich die Optionen, mit denen das Programm mount vom Automounter aufgerufen wird. Das dritte Feld beschreibt den einzuhängenden Datenträger. Dabei wird zunächst der Name des Rechners angegeben, der den Datenträger bzw. ein Verzeichnis über NFS zur Verfügung stellt. Hinter dem Doppelpunkt wird der Name des Verzeichnisses auf dem fernen Rechner angegeben. Datenträger, die an den lokalen Rechner angeschlossen sind (lokale Disketten oder CD/DVDs), erhalten keinen Eintrag vor dem Doppelpunkt und dahinter den Namen der Gerätedatei, die den einzuhängenden Datenträger darstellt.
Die Datei ist an die lokalen Gegebenheiten anzupassen. Wenn der Automounter dazu genutzt werden soll, CD/DVDs und Disketten automatisch einzuhängen, sollte sie nur noch die beiden mit cd und floppy beginnenden Einträge enthalten. Der Name
2.9 Datenträger
155
der Gerätedatei, welche das CD/DVD-Laufwerk darstellt, ist ebenfalls anzupassen. Beispiel: cd floppy
-fstype=iso9660,ro -fstype=auto
:/dev/hda :/dev/fd0
Nach jeder Änderung einer Konfigurationsdatei des Automounters muss dieser neu gestartet werden. Dies geschieht mit dem Skript /etc/init.d/autofs, das auch während des Systemstarts aufgerufen wird: debian:~# /etc/init.d/autofs restart Arbeiten mit dem Automounter Nachdem der Automounter konfiguriert und erneut gestartet wurde, kann auf die konfigurierten Verzeichnisse zugegriffen werden. Im Gegensatz zum manuellen Verfahren existieren die Verzeichnisse erst dann, wenn die Datenträger tatsächlich eingebunden werden. Wenn Sie das Programm wie oben beschrieben konfiguriert haben, zeigt folgendes Kommando ein zunächst leeres Verzeichnis an: joe@debian:~$ ls /misc Wenn sich jedoch eine Diskette mit Daten im Laufwerk befindet, führt das nächste Kommando dazu, dass die Diskette automatisch eingebunden und ihr Inhalt angezeigt wird: joe@debian:~$ ls /misc/floppy Oft wird jedoch gewünscht, dass die Verzeichnisse auch sichtbar sind, wenn der Datenträger noch nicht eingebunden ist. Hierzu kann man sich mit symbolischen Links behelfen. Beispielsweise könnte das Verzeichnis /floppy gelöscht werden und stattdessen ein symbolischer Link mit dem Namen /floppy auf das Verzeichnis /misc/floppy erzeugt werden: debian:~# rmdir /floppy debian:~# ln -s /misc/floppy /floppy Danach führt jeder Zugriff auf /flopyy dazu, dass in Wirklichkeit auf das Verzeichnis /misc/floppy zugegriffen wird und der Datenträger dadurch automatisch eingebunden wird, falls noch nicht geschehen. Die Verwendung des Automounters ändert nichts an der Tatsache, dass Disketten oder andere Medien erst dann aus dem Laufwerk genommen werden dürfen, wenn sie nicht mehr in das Dateisystem eingebunden sind. Es muss immer abgewartet werden, bis der Automounter den Datenträger aus dem Dateisystem entfernt hat. Dies lässt sich mit den Kommandos df oder mount prüfen. Alternativ kann der Automounter mit folgendem Kommando dazu gebracht werden, alle Datenträger, die nicht in Benutzung sind, sofort aus dem Dateisystem zu entfernen: debian:~# killall -USR1 automount
156
2 Linux/UNIX
Weil das Kommando nur vom Verwalter benutzt werden darf (der AutomountProzess gehört dem Verwalter), empfiehlt sich die Verwendung des Paketes sudo, um auch gewöhnlichen Benutzern zu erlauben, das Kommando auszuführen. Dazu ist der Datei /etc/sudoers folgende Zeile hinzuzufügen: ALL ALL=NOPASSWD:/usr/bin/killall -USR1 automount Gewöhnliche Benutzer können das sofortige Entfernen unbenutzter Datenträger dann durch dieses Kommando erzwingen: joe@debian:~$ sudo killall -USR1 automount Benutzerschreibrechte Ein weiteres Problem bei der Verwendung des Automounters ergibt sich daraus, dass der eigentliche Einhängevorgang durch den Automounter vollzogen wird. Weil beispielsweise DOS-formatierte Datenträger keine Besitzer oder Gruppen zu Dateien speichern, gehören Dateien auf solchen Datenträgern immer dem Benutzer, der den Datenträger eingehängt hat, es sei denn, beim Einhängen wird etwas anderes angegeben. Weil der Automounter-Prozess dem Verwalter gehört, gehören Dateien auf automatisch eingebundenen Disketten, die DOS-formatiert sind, dem Verwalter, und normale Benutzer können sie nicht verändern. In der Regel wird jedoch ein Verhalten erwünscht, dass es dem Benutzer, der auf eine solche Diskette zugreift, auch gestattet, Dateien auf der Diskette zu ändern oder neue anzulegen. Dies kann auf zweierlei Weise erreicht werden. Eine Möglichkeit ist, den Automounter so zu konfigurieren, dass er Disketten immer mit Schreib- und Leserechten für einen bestimmten Benutzer konfiguriert. Der Eintrag in der Datei /etc/auto.misc sieht dann so aus: floppy -fstype=auto,uid=1000,gid=1000 :/dev/fd0 Die Mount-Optionen uid und gid bewirken, dass der Datenträger immer mit der angegebenen User- und Gruppen-ID eingehängt wird. Die Benutzer- und GruppenID eines bestimmten Benutzers ermitteln Sie mit dem Kommando id. Sie finden sie auch in den Dateien /etc/passwd und /etc/group. Dieses Verfahren ist jedoch zu unflexibel, wenn verschiedene Benutzer auf den Datenträger zugreifen sollen. Dann empfiehlt es sich, den Datenträger mit der Gruppen-ID einer Gruppe einzuhängen, in die alle Benutzer des Systems aufgenommen werden, denen der schreibende Zugriff erlaubt sein soll: floppy -fstype=auto,gid=25,umask=002,quiet :/dev/fd0 Die Gruppen-ID 25 entspricht per Vorgabe der Gruppe floppy, und die Option umask=002 bewirkt, dass auch Gruppenmitglieder auf den Datenträger schreiben dürfen. Durch die Option quiet wird erreicht, dass Versuche, Besitzer oder Gruppe auf dem Datenträger zu ändern, nicht fehlschlagen, obwohl sie nicht durchgeführt werden können, weil DOS kein Benutzerkonzept kennt.
2.10 Suchen nach Dateien
157
2.10 Suchen nach Dateien 2.10.1 Reguläre Ausdrücke Reguläre Ausdrücke (regular expressions, RE) sind ein mächtiges Werkzeug zur Bearbeitung von Textdateien. Es handelt sich dabei um eine Erweiterung herkömmlicher Suchfunktionen von Textwerkzeugen. Programme, die mit regulären Ausdrücken umgehen, sind Texteditoren wie vi oder (X)Emacs, die Programminterpreter perl oder awk, der Streameditor sed oder das Suchprogramm grep. Die Werkzeuge arbeiten grundsätzlich zeilenweise. Die Bezeichnung stammt aus der Theorie formaler Sprachen (Programmiersprachen). Für uns reicht es zu wissen, dass reguläre Ausdrücke Zeichenfolgen sind, die nach einem System von Regeln ausgewertet werden und wiederum Zeichenfolgen oder Textmuster beschreiben. Reguläre Ausdrücke sind von den Metazeichen (Jokerzeichen) in Dateinamen zu unterscheiden, die von der Bash und anderen Programmen benutzt werden. Beide dienen zwar einem ähnlichen Zweck, haben aber eine verschiedene Syntax. Die einfachste Form eines regulären Ausdrucks ist eine gewöhnliche Zeichenkette (E: character string, F: chaîne de caractères) ohne tiefere Bedeutung, eine Zeichenketten-Konstante, ein Literal. Um die Systemprotokolldatei /var/log/ syslog nach Zeilen zu durchsuchen, welche die Zeichenkette ippp0 enthalten, wird das Werkzeug grep wie folgt benutzt: debian:~# grep ippp0 /var/log/syslog Es werden alle Zeilen ausgegeben, die eine Zeichenkette enthalten, auf die der reguläre Ausdruck ippp0 zutrifft. Was machen Sie aber, wenn Sie alle Zeilen sehen möchten, in denen ippp0 oder ippp1 vorkommt? Sie könnten den obigen Aufruf ändern und sich beim zweiten Mal die Zeilen mit ippp1 ausgegeben lassen. Sie hätten dann zwei getrennte Datensätze, aus denen sich die Reihenfolge ihres Auftretens in der Ausgangsdatei /var/log/syslog nicht mehr erkennen lässt. Reguläre Ausdrücke bieten die Möglichkeit, Suchbegriffe anzugeben, die auf mehrere Zeichenketten zutreffen. Hierzu werden bestimmte Zeichen mit besonderer Bedeutung eingesetzt. Beispielsweise wird in eckigen Klammern angegeben, dass mehrere Zeichen als zutreffend gelten sollen. Um die Datei /var/log/syslog nach ippp0 und ippp1 zu durchsuchen, wird folgendes Kommando benutzt: debian:~# grep "ippp[01]" /var/log/syslog Die Anführungszeichen sind notwendig, damit die eckigen Klammern nicht von der Shell verarbeitet werden. Wichtige Sonderzeichen in regulären Ausdrücken sind: •
•
. Ein Punkt steht für genau ein beliebiges Zeichen. Der reguläre Ausdruck a.a trifft sowohl auf die Zeichenkette aba als auch auf aca zu. Er trifft nicht auf die Zeichenkette aa zu, weil in dieser Zeichenkette das dritte Zeichen fehlt. * Ein Stern nach einem regulärem Ausdruck bedeutet ein beliebig häufiges Vorkommen – auch nullmalig – des voran stehenden regulären Ausdrucks. Der reguläre Ausdruck "stop*" trifft auf die Zeichenketten sto, stop und stopp, nicht aber auf die Zeichenkette stau zu, weil hier das dritte Zeichen kein „o“
158
•
•
•
•
2 Linux/UNIX
ist. Durch die Kombination von Punkt und Stern lassen sich beliebig lange Zeichenketten, die aus jedem Zeichen bestehen dürfen, beschreiben. Der reguläre Ausdruck .* trifft auf alle Zeichenketten zu: Ein beliebiges Zeichen darf beliebig oft vorkommen. + Das Plus-Zeichen bedeutet, dass der voran stehende reguläre Ausdruck mindestens einmal vorkommen muss. Der Ausdruck stop+ trifft deswegen nicht auf die Zeichenkette sto zu, weil das Zeichen „p“ hier nicht enthalten ist, wohl aber auf stop oder stopp. Das Pluszeichen wird nicht von allen Programmen, die mit regulären Ausdrücken umgehen, als Sonderzeichen interpretiert. [] Durch eckige Klammern eingeschlossene Zeichen bedeuten ein Zeichen aus der eingeklammerten Zeichenmenge. Beispielsweise trifft der Ausdruck st[ao].* sowohl auf stop als auch auf stau zu, nicht aber auf stumm, weil das dritte Zeichen entweder ein „a“ oder ein „o“ sein muss. Die eingeklammerte Zeichenmenge kann als Bereich angegeben werden. Der Ausdruck [0-9] umfasst alle Ziffern und der Ausdruck [a-z] alle Kleinbuchstaben ohne Umlaute. Auch hier ist die Kombination mit anderen Sonderzeichen möglich. So trifft der Ausdruck [a-z]* auf solche Zeichenketten zu, in denen beliebig viele Kleinbuchstaben oder gar keine Zeichen vorkommen. Wird den Zeichen in eckigen Klammern ein Zirkumflex (^) vorangestellt, dann ist gemeint, dass der entsprechende Ausdruck nur auf solche Zeichen zutrifft, die nicht in den eckigen Klammern enthalten sind (Komplement). Der Ausdruck [^0-9] trifft nur auf Zeichen zu, die keine Ziffern sind. ^ Um den Anfang einer Zeile anzugeben, ist ebenfalls das Zeichen ^ zu verwenden. Der Ausdruck ^Einleitung trifft für alle Zeilen zu, in denen das Wort Einleitung am Anfang der Zeile steht. $ Ebenso kann das Ende einer Zeile spezifiziert werden. Hierzu ist das Dollarzeichen zu benutzen. Der Ausdruck 2000$ trifft für solche Zeilen zu, in denen die Zahl 2000 am Ende der Zeile steht. Durch die Kombination von Zirkumflex und Dollarzeichen lässt sich der Inhalt ganzer Zeilen angeben: ^Heute ist Sonntag$ trifft ausschließlich auf solche Zeilen zu, die nur die Zeichenkette Heute ist Sonntag beinhalten, und der Ausdruck: ^Heute ist .*tag$
•
trifft auf die Zeilen zu, die mit der Zeichenkette Heute ist beginnen und mit den Zeichen tag enden, also auch auf Heute ist Dienstag oder Heute ist ein besonders warmer Donnerstag, nicht aber auf Heute ist ein schöner Tag, weil die letzte Zeile nicht mit den Zeichen tag, sondern mit Tag endet. \ Gelegentlich müssen Zeichen mit besonderer Bedeutung wie normale Zeichen (buchstäblich) eingesetzt werden. Wir kennen das bereits unter dem Begriff Quoten von den Metazeichen der Shell. Ein vorangestellter Gegenschrägstrich nimmt den Sonderzeichen ihre besondere Bedeutung. Der Punkt in Danke\. hat keine besondere Bedeutung mehr, der Ausdruck steht für die Zeichenkette Danke.
2.10 Suchen nach Dateien
•
159
(mit Punkt). Wenn das Zeichen \ als normales Zeichen genutzt werden soll, ist ihm ebenfalls ein Gegenschrägstrich voranzustellen (\\). | Zwei durch einen senkrechten Strich miteinander verbundene reguläre Ausdrücke bilden einen regulären Ausdruck, der auf jede Zeichenkette zutrifft, die auf mindestens einen der beiden Teilausdrücke passt. Dies ist eine Erweiterung, die beispielsweise nicht von grep (ohne Optionen), wohl aber von egrep verstanden wird.
Wenn Sie in der Kommandozeile mit regulären Ausdrücken arbeiten, sollten Sie wegen der Ähnlichkeit mit den Metazeichen der Shell die regulären Ausdrücke mit einfachen Anführungszeichen (single quotes) vor jeder Verabeitung durch die Shell schützen. Nehmen Sie irgendeinen Text und lassen Sie das Kommando grep mit verschiedenen regulären Ausdrücken darauf los: joe@debian:~$ grep ’aber’ textfile joe@debian:~$ grep ’ab.a’ textfile joe@debian:~$ grep ’bb.[aeiou]’ textfile joe@debian:~$ grep ’ˆ[0-9]+$’ textfile joe@debian:~$ grep ’M[ae][iy]e?r’ textfile grep gibt die Zeilen aus, in denen sich wenigstens eine Zeichenkette befindet, auf die der reguläre Ausdruck passt. Im ersten Beispiel sind das alle Zeilen, welche die Zeichenkette aber enthalten wie aber, labern, Schabernack, aberkennen, im zweiten trifft unter anderem abwarten zu, im dritten Abbruch. Die vierte Form ermittelt alle Zeilen, die nur Ziffern enthalten: Am Anfang der Zeile (Circumflex) ein Zeichen aus der Menge 0 bis 9, dann eine beliebige Wiederholung von Ziffern bis zum Ende (Dollar) der Zeile. Man sagt, dass Circumflex oder Dollarzeichen das Muster am Zeilenanfang oder -ende verankern. In der fünften Zeile wird nach dem Namen Meier mit all seinen Varianten geforscht. Wie lautet ein regulärer Ausdruck, der in einer Liste auf die Namen aller .exe-Programme aus der DOS-Welt zutrifft? Ganz einfach: \.exe$ Der Punkt muss mittels Backslash seiner besonderen Bedeutung beraubt (gequotet) werden, dann folgen drei harmlose Buchstaben. Das Dollarzeichen besagt, dass die vorgenannte Zeichenfolge am Ende einer Zeile (Dateinamen) vorkommen soll. Wollen wir auch groß geschriebene Dateinamen erwischen, geht das mit einer oderVerknüpfung: \.exe$|\.EXE$ Wir wollen nun einen regulären Ausdruck zusammenstellen, der auf alle gültigen Internet-Email-Anschriften zutrifft. Dazu schauen wir uns einige Anschriften an:
160
2 Linux/UNIX
[email protected] [email protected] [email protected] [email protected] [email protected] Links steht immer ein Benutzername, dessen Form vom jeweiligen Betriebssystem (Eintrag in /etc/passwd oder /etc/aliases) bestimmt wird, dann folgen das @-Zeichen (Klammeraffe) und ein Maschinen- oder Domänenname, dessen Teile durch Punkte voneinander getrennt sind. Im einzelnen: • • • • • • •
Anfangs ein Zeichen aus der Menge der Ziffern oder kleinen oder großen Buchstaben, dann eine beliebige Anzahl einschließlich null von Zeichen aus der Menge der Ziffern, der kleinen oder großen Buchstaben und der Zeichen _-., genau ein Klammeraffe als Trennzeichen, im Maschinen- oder Domänennamen mindestens eine Ziffer oder ein Buchstabe, dann eine beliebige Anzahl von Ziffern, Buchstaben oder Strichen, mindestens ein Punkt zur Trennung von Domäne und Top-Level-Domäne, nochmals mindestens ein Buchstabe zur Kennzeichnung der Top-Level-Domäne.
Daraus ergibt sich folgender regulärer Ausdruck (einzeilig): ^[0-9a-zA-Z][0-9a-zA-Z_-.]*@[0-9a-zA-Z][0-9a-zA-Z_-.]* \.[a-zA-Z][a-zA-Z]* Das sieht kompliziert aus, ist aber der einfachste Weg zur Beschreibung solcher Gebilde. Obige Form einer Email-Anschrift ist nicht gegen die RFCs abgeprüft und daher vermutlich zu eng. Eine Anwendung für den regulären Ausdruck könnte ein Programm sein, das Email-Anschriften verarbeitet und sicherstellen will, dass die ihm übergebenen Zeichenketten wenigstens ihrer Form nach gültig sind. Robuste Programme überprüfen Eingaben, ehe sie sich weiter damit abgeben. Nähere Informationen zu regulären Ausdrücken finden sich in der InfoDokumentation zum Programm awk (GNU Version, Paket gawk), in der Manualseite zum regulären Ausdruck nach dem POSIX-Standard (Manualseite zu regex in Abschnitt 7) sowie in der Manualseite zu dem Editor ed (Paket ed). 2.10.2 Suchen mit find Aufruf von find Zum Suchen und Finden von Dateien dient das Kommando find. Es ermöglicht, Dateien anhand unterschiedlicher Kriterien zu suchen, die auch miteinander verknüpft werden können. Außerdem können mit den gefundenen Dateien Aktionen durchgeführt werden. Beispielsweise kann untersucht werden, ob sie eine bestimmte Zeichenfolge enthalten oder nicht. In einfachen Fällen sieht die Syntax von find so aus:
2.10 Suchen nach Dateien
161
find [Verzeichnis] Suchkriterium [Argument] Verzeichnis bezeichnet dabei das Verzeichnis, das rekursiv durchsucht werden soll. Die Dokumentation von Debian befindet sich unterhalb des Verzeichnisses /usr/share/doc. Wenn hieraus ein Dokument gesucht werden soll, so sucht man nur unterhalb des Verzeichnisses, um den Suchvorgang abzukürzen. Fehlt die Angabe des Verzeichnisses, wird das Arbeitsverzeichnis genommen. Suchkriterium stellt ein Argument dar, das find angibt, wonach zu suchen ist. Bei den Suchkriterien handelt es sich um Tests, die für jede Datei unterhalb des Startverzeichnisses durchgeführt werden. Ein Beispiel für einen Test ist das Suchkriterium -name, welches find veranlasst, für jede Datei zu überprüfen, ob sie einen bestimmten Namen hat. Den Suchkriterien muss in den meisten Fällen noch ein weiteres Argument folgen, nämlich das Argument des Kriteriums. Im Beispiel von -name ist das der Dateiname, nach dem gesucht werden soll. Um alle Dateien zu finden und anzuzeigen, die unterhalb des Verzeichnisses /usr/share/doc liegen und den Namen README tragen, ist folgendes Kommando einzugeben: joe@debian:~$ find /usr/share/doc -name README Früher musste noch die Aktion -print angegeben werden, aber die ist heute vorgegeben und darf entfallen. Neben -name gibt es noch eine Reihe weiterer Suchkriterien. Die wichtigsten sind: •
•
-iname Dateiname Es wird nach einem Dateinamen ohne Beachtung von Groß- und Kleinschreibung gesucht (das i steht für case insensitive). Beispiel: wird nach GPL gesucht, wird auch gpl gefunden. -user Benutzername und -group Gruppenname Es wird nach Dateien gesucht, die einem bestimmten Benutzer oder einer bestimmten Gruppe zugeordnet sind. Beispiel: joe@debian:~$ find -user root
•
Es werden die Namen aller Dateien ausgehend vom aktuellen Verzeichnis ausgegeben, die dem Systemverwalter gehören. -newer Datei Es werden Dateien gefunden, die später als die mit Datei angegebene Datei geändert wurden. Beispiel: joe@debian:~$ find -newer /etc/passwd
•
Es werden ausgehend vom Arbeitsverzeichnis alle Dateien ausgegeben, die nach dem Datum verändert wurden, an dem die Datei /etc/passwd zuletzt geändert oder neu erzeugt wurde. -type Typ Es werden die Dateien ausgegeben, die vom Typ Typ sind. Für Typ kann Folgendes eingesetzt werden: – d Verzeichnis (directory). Es werden nur Verzeichnisse ausgegeben. – l Symbolischer Link. Es werden nur symbolische Links ausgegeben. – f Datei (file). Es werden nur gewöhnliche Dateien ausgegeben.
162
•
2 Linux/UNIX
-size Größe Es werden Dateien ausgegeben, deren Größe Größe überschreitet, wenn Größe das Zeichen „+“ vorangestellt ist, oder deren Größe Größe unterschreitet, wenn Größe ein „-“ vorangestellt ist. Wenn Größe weder ein „+“ noch ein „-“ vorangestellt ist, werden nur die Namen der Dateien ausgegeben, deren Größe Größe exakt entspricht. Hinter der Angabe der Größe kann die Einheit angegeben werden. Dafür gibt es folgende Möglichkeiten: – c Die Größe wird in Bytes angegeben (count). – k Die Größe wird in KB (Kilobyte) angegeben. – b Die Größe wird in Blöcken angegeben. Das folgende Kommando sucht unterhalb des aktuellen Arbeitsverzeichnisses alle Dateien, die kleiner als 5 Kilobyte sind: joe@debian:~$ find -size -5k Das nächste Kommando listet alle Dateien unterhalb des Verzeichnisses /usr/share/doc auf, die größer als 100 KB sind: joe@debian:~$ find /usr/share/doc -size +100k
Das Kommando find ist vielseitig, braucht aber seine Zeit für die Ausführung. Es ist möglich, Suchkriterien durch logische Operatoren zu verbinden. Hierzu stehen die Operatoren -and, -or und -not zur Verfügung. Beispielsweise sucht folgendes Kommando nach Dateien unterhalb des Verzeichnisses /usr/share/ doc, deren Namen README lautet und deren Größe 4 KB übersteigt: joe@debian:~$ find /usr/share/doc -name README -and -size +4k Das nächste Kommando sucht unterhalb des aktuellen Arbeitsverzeichnisses alle Dateien, die nicht dem Systemverwalter gehören und gewöhnliche Dateien (keine Verzeichnisse oder symbolische Links) sind: joe@debian:~$ find -not -user root -and -type f Wenn Sie find mit den Suchkriterien -name oder -iname benutzen, dürfen die Namen, nach denen gesucht wird, auch Metazeichen enthalten. Diese haben die gleiche Bedeutung wie für die Shell. Bedenken Sie jedoch, dass Sie die Shell daran hindern müssen, die Metazeichen zu verarbeiten. Quoten Sie die Zeichenkette, wenn Sie möchten, dass das Metazeichen unverändert an find übergeben wird: joe@debian:~$ find /usr/share/doc "GPL*" Ausführen von Kommandos mit den Suchergebnissen Als Voreinstellung gibt find den Pfad- und Dateinamen zu einer Datei aus, die den Suchkriterien entspricht. Gelegentlich ist es jedoch erwünscht, die Suchergebnisse gleich einem weiteren Kommando zuzuführen. Beispielsweise erzeugen viele Programme eine core-Datei, wenn ein schwerer Fehler auftritt. Die Cores belegen viel Platz und helfen dem durchschnittlichen Benutzer nichts, weg damit. Dies wird erreicht, indem man find nach core-Dateien suchen und dann das Programm rm
2.10 Suchen nach Dateien
163
direkt von find aufrufen lässt. Zunächst wollen wir find aber nur das ungefährliche Programm ls mit dem Argument -l aufrufen lassen, um nicht Dateien zu löschen, sondern die Größe der Dateien anzeigen zu lassen. Zum Ausführen eines Programms durch find dient das Argument -exec. Ihm muss der Name des Programms übergeben werden, das aufgerufen werden soll. Alle folgenden Parameter übergibt find dem aufzurufenden Programm. Wird -exec ls -l angegeben, so ruft find ls mit der Option -l auf. Deswegen muss find wissen, wo die Parameter für das aufzurufende Programm enden. Dies wird mit einem Semikolon angezeigt. Hierbei ist zu beachten, dass das Semikolon für die Shell eine besondere Bedeutung hat (Metazeichen) und durch einen Backslash gequotet werden muss. Das Argument lautet dann -exec ls -l \;. Das ist noch nicht alles. Bis jetzt würde find für jede gefundene Datei das Kommando ls -l ausführen, wodurch die Dateien im Arbeitsverzeichnis angezeigt werden würden. Dies würde genau so oft passieren, wie find Dateien findet, auf die die angegebenen Suchkriterien zutreffen. Da dies keinen Sinn ergibt, muss find an ls noch ein Argument übergeben, das ls mitteilt, welche Datei anzuzeigen ist. Hierfür wird ein Paar geschweifter Klammern benutzt, an deren Stelle find später die Namen der gefundenen Dateien einsetzt. Wird find der Parameter -exec ls -l {} \; übergeben und dann die Datei texte/core gefunden, so ruft find ls folgendermaßen auf: joe@debian:~$ ls -l texte/core Um die gefundenen Dateien automatisch durch rm löschen zu lassen, ist dieser Aufruf zu verwenden: joe@debian:~$ find ~/ -name core -exec rm {} \; Alternativ zum Argument -exec kann das Argument -ok verwendet werden. Es bewirkt das Gleiche, dabei wird vor jedem Aufruf des auszuführenden Kommandos nachgefragt, ob es tatsächlich ausgeführt werden soll. Das wird dann mitoder bestätigt oder verneint. Die Syntax von find variiert etwas zwischen den verschiedenen Linux/UNIXSystemen, im Manual nachlesen. Dem Microsoft-Kommando find entspricht das Linux/UNIX-Kommando grep. 2.10.3 Schnelles Suchen mit locate Einen anderen Ansatz als find verfolgt das Kommando locate. Während find das Dateisystem nach jedem Aufruf tatsächlich durchsucht, benutzt locate eine Datenbank, in der Namen und Verzeichnisse der meisten Dateien auf dem System gespeichert sind. Diese Datenbank wird üblicherweise einmal pro Nacht durch Aufruf des Programms updatedb aktualisiert16 Deswegen werden Dateien, die gerade
16
Verantwortlich hierfür ist der cron-Dämon, siehe Abschnitt 10.3.3 Zeitdämon auf Seite 446.
164
2 Linux/UNIX
eben erzeugt wurden, mit locate nicht gefunden. locate eignet sich gut zur Suche nach Dateien, die Bestandteil der Debian-Installation sind, weil diese sich nicht täglich ändert. Im einfachsten Fall rufen Sie locate folgendermaßen auf: locate Suchzeichenfolge Die Suchzeichenfolge bezeichnet dabei eine Zeichenkette, die im Datei- oder Pfadnamen der gesuchten Datei vorkommt. Beispielsweise sucht folgendes Kommando nach allen in der Datenbank gespeicherten Dateien, in deren Namen die Zeichenkette README vorkommt und listet diese auf: joe@debian:~$ locate README Sie können in der Suchzeichenfolge auch die Shell-Metazeichen *, ? und [] verwenden. Schützen Sie die Metazeichen vor der Auswertung durch die Shell. Wenn Sie Metazeichen verwenden, werden nur Dateien ausgegeben, auf die die Suchzeichenfolge exakt zutrifft. Beispiel: die Suchzeichenfolge aa* trifft nicht exakt auf den Namen laang zu, weil das l mit ihr nicht abgedeckt ist, während ohne Metazeichen alle Dateien ausgegeben werden, die die Suchzeichenfolge enthält. So durchsucht folgendes Kommando die Datenbank nach allen Einträgen, die mit der Zeichenfolge README enden: joe@debian:~$ locate "*README" Das nächste Kommando gibt alle Dateien der Datenbank aus, die die Zeichenfolge README enthalten, gefolgt von einem Punkt und dann drei beliebigen Zeichen: joe@debian:~$ locate "*README.???" Wir wollen nun in allen Dateien namens socket.h nach der Zeichenfolge protocol family suchen. Dazu bauen wir eine Pipe aus grep und locate auf: joe@debian:~$ grep "protocol family"‘locate socket.h‘ locate liefert die Liste der Dateien, der in rückwärtsgerichteten Hochkommas (back quote) stehende Ausdruck wird samt den Hochkommas durch die Liste ersetzt (substituiert) und grep als zweites Argument untergejubelt. Das erste Argument steht in Anführungszeichen, damit es von der Shell als ein einziger Ausdruck weitergereicht wird. Die locate-Datenbank kann auf bestimmte Verzeichnisse begrenzt werden, sodass beispielsweise die Home-Verzeichnisse der Benutzer nicht erfasst werden. locate arbeitet schnell, da die Suche im Dateisystem bereits durch updatedb erledigt ist, das eine gewisse Zeit beansprucht.
2.11 Textbetrachter (Pager) 2.11.1 Aufgabe Von den zahlreichen Anwendungen unter Debian GNU/Linux greifen wir hier nur einige Textbetrachter und anschließend zwei Texteditoren heraus, weil sie bei der Systemverwaltung unentbehrlich sind. Ein Textbetrachter oder Pager ist ein Programm,
2.11 Textbetrachter (Pager)
165
das Textdateien am Bildschirm seitenweise ausgibt und dem Benutzer erlaubt, sich im Text zu bewegen, also vorwärts oder rückwärts zu blättern oder im Text nach Zeichenketten zu suchen. Ein Pager, der auf jedem Linux/UNIX System zu finden sein sollte, ist das klassische Programm more. Unter Debian GNU/Linux steht zusätzlich das Programm less zur Verfügung, das mehr Funktionen kennt und bequemer zu benutzen ist als more. Verschiedene Betriebssysteme markieren den Zeilenwechsel in Textdateien mittels unterschiedlicher Zeichen: • • •
In der Linux/UNIX-Welt mittels des ASCII-Zeichens Nr. 10 (LF, Zeilenvorschub, E: line feed, F: saut de ligne), in der traditionellen Macintosh-Welt mittels des ASCII-Zeichens Nr. 13 (CR, Wagenrücklauf, E: carriage return, F: retour chariot), in der DOS/Windows-Welt mittels des Paares aus beiden Zeichen (CR LF).
Manche Werkzeuge übersetzen die Zeilenwechsel stillschweigend, manche nicht. Bei diesbezüglichen Problemen helfen Übersetzer wie todos und fromdos aus dem Paket sysutils, der allgemeine Zeichenersetzer tr, der Zeichensatz-Konverter recode oder der Streaming Editor sed. Programme zum Betrachten von Text sind Bestandteil des Alternativen-Systems von Debian. Sie können mehrere solche Programme gleichzeitig einrichten. Welches Programm dann nach Eingabe des Kommandos pager aufgerufen wird, wird durch einen symbolischen Link im Verzeichnis /etc/alternatives festgelegt, siehe Abschnitt 9.1.7 Arbeiten mit Alternativen auf Seite 394. Meist ist pager ein Symlink auf /usr/bin/less. Die Textbetrachter können natürlich auch direkt über ihren eigentlichen Namen wie less aufgerufen werden. Eine Reihe von Programmen, die einen Textbetrachter verwenden, werten den Inhalt der Umgebungsvariable PAGER aus (siehe Abschnitt 2.7.5 Benannte Variable auf Seite 77), um zu entscheiden, welches Programm tatsächlich aufgerufen werden soll. Das bekannteste Beispiel hierfür ist das Programm man. Textbetrachter können in zwei verschiedenen Modi arbeiten: Sie zeigen den Text aus einer Datei an, oder sie lesen den Text aus einem Datenstrom, einer Pipe (siehe Abschnitt 2.7.2 Kommandoverkettung auf Seite 66). Um beispielsweise die Datei readme.txt im Arbeitsverzeichnis mittels less anzuzeigen, kann alternativ einer der beiden Aufrufe benutzt werden: joe@debian:~$ less readme.txt joe@debian:~$ cat readme.txt | less In obigem Zusammenhang ist die erste Fassung einfacher, aber es gibt auch Fälle, in denen man die Pipe braucht: joe@debian:~$ ps -ef | less Schließlich kann man mit Texteditoren auch Text lesen, ohne ihn zu ändern. Wer den vi in den Fingerspitzen hat, ruft ihn zum Lesen in der Form view auf und verfügt über alle Funktionen des Editors. Solange es nur um das Blättern in einem nicht zu
166
2 Linux/UNIX
langen Text geht, bietet auch das Scrollen im Bildschirmspeicher einen Weg, sich den Text häppchenweise anzusehen, aber die sonstige Funktionalität der Pager wie Suchen nach Zeichenketten fehlt. In vorstehendem Beispiel umfasst die Ausgabe etwa hundert Zeilen entsprechend vier Bildschirmen, da ist auch Scrollen eine Möglichkeit. Will man sich nur die ersten oder letzten Zeilen einer Textdatei ansehen, sind die Kommandos head und tail die richtigen Werkzeuge. Der Aufruf: joe@debian:~$ tail /var/log/messages zeigt die jüngsten zehn Zeilen der Protokolldatei zum Zeitpunkt des Aufrufs an. Die Anzahl der auszugebenden Zeilen lässt sich als Option mitgeben: joe@debian:~$ tail -20 /var/log/messages genauso für head. Mittels (f = follow): joe@debian:~$ tail -f /var/log/messages wird die Ausgabe laufend nachgeführt und somit die Datei ständig zum Lesen offen gehalten. 2.11.2 Benutzung von more Die Programme more und less werden beide mit der Tastatur benutzt. Bei more sind die wichtigsten Tastaturkommandos:oder > <space> + oder
Eine Hilfe mit den Tastaturkommandos wird angezeigt. Die nächste Seite des Textes wird angezeigt. (back) Die vorige Seite des Textes wird angezeigt. Eine weitere Zeile wird angezeigt. Der Bildschirm wird neu geschrieben. (quit) Das Programm wird verlassen.
Darüber hinaus bietet more weitere Kommandos und Funktionen wie Suchen nach regulären Ausdrücken an, die zum Teil jedoch nur dann funktionieren, wenn mit dem Programm eine Datei angezeigt wird und nicht, wenn more aus einem Datenstrom liest, wie es bei der Verwendung des Programms mit man der Fall ist. Ein Datenstrom kann nicht rückwärts fließen. 2.11.3 Benutzung von less Diese und andere Schwächen von more beseitigt das Programm less, mit dem Sie auch in Text aus einem Datenstrom vor- und zurückblättern können. Die wichtigsten Kommandos für die Arbeit mit less werden nachfolgend aufgeführt. Vielen Kommandos kann dabei optional eine Zahl vorangestellt werden. Dazu ist zuerst die Zahl einzugeben und dann unmittelbar folgend die Taste für das Kommando zu betätigen. Die Liste aller von less verstandenen Kommandos finden Sie im Manual sowie in der Online-Hilfe.
2.11 Textbetrachter (Pager)
oder <space> oder
b oder <esc>+
<w>oder <e> oder <esc>+<(> <esc>+<)> +
>
><s> oder
167
Eine Hilfe mit den Tastaturkommandos wird angezeigt. Im Text wird um eine voreingestellte Anzahl von Zeilen vorwärts geblättert. In der Regel ist das die Menge Text, die auf einen Bildschirm passt. Stellen Sie eine Zahl voran, wird um die angegebene Anzahl Zeilen vorwärts geblättert. Wie <space> odermit dem Unterschied, dass die optional vorangestellte Zahl zur neuen Voreinstellung wird. Rückwärts blättern um die voreingestellte Menge Text. Diese ist wie bei space voreingestellt auf die Menge Text, die auf einen Bildschirm passt. Wie mit dem Unterschied, dass die optional vorangestellte Zahl zur neuen Voreinstellung wird. Ohne vorangestellte Zahl wird um eine Zeile vorgeblättert, ansonsten um die angegebene Anzahl von Zeilen. Rückwärts blättern um eine Zeile oder die spezifizierte Anzahl von Zeilen. Defaultmäßig ein halber Bildschirm vorwärts, sonst die angegebene Anzahl von Zeilen. Wie , jedoch rückwärts. Der Text wird um die angegebene Anzahl von Spalten (Zeichen) nach rechts verschoben, ohne Angabe um acht. Der Text wird um die angegebene Anzahl von Spalten nach links verschoben, ohne Angabe um acht. Der Bildschirm wird neu gezeichnet. Es wird zu der Textstelle gesprungen, die der angegebenen Menge des Textes in Prozent entspricht. Wenn Sie 50 und dann eingeben, gelangen Sie zur Mitte des Textes. Suchen. Nach dem Schrägstrich ist ein regulärer Ausdruck einzugeben und abschließend
zu drücken. less sucht nach Zeichenfolgen, die zum RE passen und springt zum ersten Treffer. Wenn Sie mehrmals nach der gleichen Zeichenfolge suchen, setzen Sie die Suche mittels Eingabe von (new) fort. Geben Sie ein, um rückwärts weiter zu suchen. Wie >, nur rückwärts. Es wird der voreingestellte Editor mit der Datei geöffnet. Das Kommando funktioniert nur bei Dateien. (save) Der Text wird in eine Datei gespeichert. Das Kommando funktioniert nur bei Datenströmen. (quit) Beendet das Programm less.
Die Pfeiltasten sowie die Tasten <Page Up> und <Page Down> funktionieren ebenfalls erwartungsgemäß. Einige Kommandos stimmen mit denen des Texteditors vi überein. Wer diesen Editor gewohnt ist, findet sich schnell zurecht.
168
2 Linux/UNIX
Manchmal ist es notwendig, less mitzuteilen, welchen Zeichensatz das Programm benutzen soll. Hierzu dient die Umgebungsvariable (siehe Abschnitt 2.7.5 Benannte Variable auf Seite 77) LESSCHARSET. Einige Werte für diese Variable sind: • •
• •
ascii Es werden nur die Zeichen des US-ASCII-Zeichensatzes angezeigt. Die deutschen Umlaute gehören nicht dazu. iso8859 Dieser Zeichensatz entspricht weitgehend dem ASCII-Zeichensatz, enthält jedoch zusätzlich die Sonderzeichen westeuropäischer Sprachen, Dieser Wert ist vorgegeben, falls die Variable LESSCHARSET nicht gesetzt ist. dos MS-DOS Zeichensatz. koi-8r Zeichensatz für die russische Sprache (kyrillische Schrift).
Um den Inhalt russischer (kyrillischer) Textdateien anzuzeigen, die mit dem Zeichensatz koi-8r kodiert sind, ist folgendes Kommando einzugeben, bevor less aufgerufen wird: joe@debian:~$ export LESSCHARSET=koi-8r Ein kyrillischer Font für den Bildschirm muss eingerichtet sein. Ohne Font geht nichts. 2.11.4 Betrachten komprimierter Dateien mit zless Fast alle Textdateien in den Verzeichnissen unterhalb von /usr/share/doc liegen in mittels gzip komprimierter Form vor. Um diese Dateien mit less zu betrachten, sind sie vorher zu dekomprimieren. Einfacher geht es mit dem Kommando zless. Das Kommando dekomprimiert die Datei und zeigt sie an: joe@debian:~$ zless /usr/share/doc/bash/FAQ.gz Neben zless steht auch das Kommando zmore zur Verfügung, welches den Inhalt komprimierter Dateien oder eines komprimierten Datenstroms mit dem Programm more anzeigt. 2.11.5 Anzeigen spezieller Daten mit lessfile und lesspipe Die beiden Programme lessfile und lesspipe erlauben es, auch Dateien mit less zu betrachten, bei denen es sich nicht um Textdateien handelt. Die Programme können von less aufgerufen werden, bevor eine Datei angezeigt wird, und die Datei so umwandeln, dass ihr Inhalt wie gewöhnlicher Text angezeigt wird. Die Programme unterscheiden sich dadurch, dass lesspipe die umgewandelten Daten direkt auf die Standardausgabe gibt, die mit der Standardeingabe von less verbunden ist, während lessfile zunächst die gesamte anzuzeigende Datei umwandelt, das Ergebnis in eine temporäre Datei schreibt und den Inhalt der Datei mit less anzeigt. Die Datei wird bei Beendigung von less gelöscht. Bei der Verwendung von lessfile muss also gelegentlich länger gewartet werden, bis Daten angezeigt werden, während lesspipe sofort Daten zur Verfügung stellt, die aber
2.12 Editoren
169
möglicherweise zu Beginn noch nicht komplett durchsucht werden können, weil sie noch nicht umgewandelt wurden. Beide Programme entscheiden anhand der Endung des Namens einer Datei, wie die betreffenden Daten umzuwandeln sind. Daraus ergibt sich, dass dieses Verfahren nur dann funktioniert, wenn der Inhalt einer Datei angezeigt wird und nicht, wenn von der Standardeingabe gelesen wird. Im zweiten Fall steht kein Dateiname zur Verfügung. Das Programm less erfährt durch die beiden Umgebungsvariablen LESSOPEN und LESSCLOSE davon, welche Programme vor und nach dem Anzeigen einer Datei aufgerufen werden sollen. Die beiden Variablen können mit den Programmen lessfile bzw. lesspipe gesetzt werden. Wenn Sie lessfile benutzen, geben Sie folgendes Kommando ein: joe@debian:~$ eval $(lessfile) Wenn Sie lesspipe benutzen, ist dieses Kommando zu verwenden: joe@debian:~$ eval $(lesspipe) Um diese Einstellung dauerhaft zu verwenden, ist das entsprechende Kommando in der Startdatei der Standardshell anzugeben (siehe Abschnitt 2.7.1 Konfiguration und Startdateien auf Seite 51). Mit dem Aufruf: joe@debian:~$ set | grep LESS ermitteln Sie, ob die beiden Umgebungsvariablen gesetzt sind. Falls ja, kann less den Inhalt sowie die Beschreibung von Debian-Paketen oder von komprimierten Dateien anzeigen. Um beispielsweise den Inhalt des Debian-Archivs grub_0.97-16.1_i386.deb anzuzeigen, ist im Verzeichnis /var/cache/ apt/archives folgendes Kommando einzugeben: joe@debian:~$ less grub_0.97-16.1_i386.deb wobei man vorteilhaft von der Dateinamenergänzung mittelsGebrauch macht.
2.12 Editoren 2.12.1 Grundbegriffe Ein Editor – wörtlich ein Herausgeber – ist im allgemeinen Sinn ein Werkzeug zum Erzeugen und Verarbeiten von Dateien. Zum Erzeugen und Verarbeiten von Textdateien wird ein Texteditor benötigt, ein Texteisen. Andere Editoren bearbeiten Formeln, Grafiken, Musiknoten, Sounddateien oder beliebige binäre Daten. Texteditoren stellen den Inhalt der zu verarbeitenden Textdatei auf dem Bildschirm dar und ermöglichen, durch Editierkommandos Text einzugeben oder den angezeigten Text zu ändern. Es lassen sich zwei Gruppen von Texteditoren unterscheiden:
170
•
•
2 Linux/UNIX
Texteditoren im engeren Sinn nehmen Zeicheneingaben und Editierkommandos entgegen, formatieren den Text aber nicht und stellen auch keinen formatierten Text auf dem Bildschirm dar (von der Zeilenstruktur abgesehen, die man kaum als Format bezeichnen kann). Sie fügen von sich aus keine verdeckten Zeichenketten in den Text ein. In der Systemverwaltung spielen nur diese Editoren eine Rolle. WYSIWYG-Editoren – auch Textprozessoren genannt – kennen Formatieranweisungen und zeigen deren Auswirkungen sofort auf dem Bildschirm an, daher der Name WYSIWYG = What You See Is What You Get, zu Deutsch: Auf dem Bildschirm sieht man, wie der Text später auf Papier aussieht, mehr oder weniger. Textprozessoren sind hauptsächlich in Büros anzutreffen.
Obwohl die Textprozessoren als die intelligenteren Werkzeuge erscheinen, sind ihnen bei anspruchsvollen Aufgaben die einfachen Texteditoren in Verbindung mit Formatier- oder Satzprogrammen überlegen. Unsere Debian-Bücher beispielsweise sind mit Texteditoren erster Art geschrieben und dann mittels des Satzprogramms LATEX formatiert worden. Ferner kann man Texteditoren ebenso wie andere Werkzeuge danach einteilen, ob sie mit einem Textbildschirm auskommen oder eine grafische Oberfläche mitbringen. Ein vergleichsweise einfacher Texteditor mit grafischer Oberfläche ist gedit. Den Emacs gibt es in beiden Geschmacksrichtungen. gvim ist eine vi-Variante mit grafischer Oberfläche. Alle Texteditoren stehen vor dem Problem, das sie über ein- und dieselbe Tastatur sowohl Text wie Editierkommandos empfangen, wobei die wenigen Editiertasten der üblichen PC-Tastatur mit etwas über 100 Tasten bei weitem nicht ausreichen. Es haben sich zwei Lösungen herausgebildet: •
•
Die Editoren der vi-Familie kennen zwei Modi. Im Eingabe- oder Schreibmodus werden alle Eingaben als zu speichernder Text aufgefasst, im Befehls- oder Kommandomodus als Editierkommandos. Die Editoren der Emacs-Familie kennen nur einen Modus (oder keinen) und unterscheiden die Editierkommandos durch besondere Tastenkombinationen vom eingegebenen Text.
Beide Wege haben Vor- und Nachteile sowie ihre Anhänger. Man sollte keinen Glaubenskrieg darum entfachen. Schreiben kann man mit beiden gleichermaßen. Unter Debian GNU/Linux stehen Ihnen verschiedene Texteditoren zur Verfügung, die jedoch nicht alle auf Ihrem Rechner eingerichtet zu sein brauchen. Die Texteditoren nehmen am Alternativen-System von Debian teil, siehe /etc/ alternatives. Das Kommando editor kann Ihren Lieblingseditor aufrufen, unabhängig davon, wie der tatsächlich heißt. Neben den oft gebrauchten Texteditoren gibt es Werkzeuge für besondere Aufgaben wie den Streaming-Editor sed, ein Filter, das einen Text nach mitgegebenen Regeln bearbeitet, oder Hex-Editoren, die eine beliebige Datei byteweise zu editieren gestatten. Auch das Mischen rechts- und linksläufiger Schriften in einem Text ist eine Aufgabe für Spezialisten. In unserem zweiten Debian-Band gehen wir auf einige dieser Editoren ein.
2.12 Editoren
171
2.12.2 Zeichensätze Wenn es um Texte geht, muss man sich zuerst mit dem Problem der Zeichensätze herumschlagen. Das hat nichts mit Linux/UNIX zu tun, sondern gilt für alle Betriebssysteme. Der Rechner kennt nur Bits und Bytes (Oktetts, Gruppen von 8 Bits). Die Bedeutung erhalten die Bits durch die Programme. Ob eine Bitfolge in der Menschenwelt eine Zahl, ein Wort oder einen Schnörkel darstellt, entscheidet die Software. Um mit Texten zu arbeiten, muss zuerst ein Zeichensatz (E: character set, F: jeu de caractères) vereinbart werden. Dieser besteht aus einer anfangs ungeordneten Menge von Zeichen, auch Répertoire oder Zeichenvorrat genannt, die nur besagt, welche Zeichen bekannt sind. Zu dem Zeichenvorrat der europäischen Sprachen gehören: • • • • • •
Kleine und große Buchstaben, auch mit Akzenten (Diakritika) usw., Ziffern, Satzzeichen, Symbole: aus der Mathematik, Euro-Symbol, Klammeraffe (E: commercial at, F: arobace), et-Zeichen, Leerzeichen (Zwischenraum, Space), Tabulator (so genannte Whitespaces, Zeichen, die einen Vorschub erzeugen, aber keine Druckerschwärze verlangen), Steuerzeichen wie Zeilenwechsel (Line Feed), Seitenwechsel (Form Feed), Backspace.
In einem zweiten Schritt wird die Menge geordnet, jedem Zeichen wird eine Position (E: code position, code point, Unicode scalar value) zugewiesen. Nahe liegend ist eine mit null beginnende Nummerierung. Die geordnete Menge ist der Zeichensatz. In der Regel bekommt jedes Zeichen auch noch einen Namen, siehe Tabelle 2.4. Wir wissen aber noch nicht, wie die Zeichen im Rechner und auf dem Bildschirm oder auf Papier dargestellt werden. Auch die Aussprache der Zeichen ist noch zu vereinbaren und nicht einheitlich. Das Zeichen ist vorläufig noch abstrakt. Tab. 2.4: Ausschnitt aus dem Zeichensatz Latin-1 Nummer Zeichen Name Nr. 008 Nr. 055 Nr. 104 Nr. 123 Nr. 220
bs 7 h { Ü
Backspace (Rückschritt) Digit seven (Ziffer sieben) Latin small letter h (lateinischer Kleinbuchstabe h) Left curly bracket (linke geschweifte Klammer) Latin capital letter u with diaresis (lateinischer Großbuchstabe u-Umlaut)
Die Zeichencodierung (E: character encoding) legt fest, wie eine Folge von Zeichen in eine Folge von Bits oder Bytes umzuwandeln ist, und zurück. Der Zeichensatz ist von seiner Codierung zu unterscheiden. Im einfachsten Fall wird die vorzeichenlose, ganzzahlige Positionsnummer als Code für ein Zeichen genommen. Dann
172
2 Linux/UNIX
stellt sich die Frage, wie solche Zahlen im Rechner dargestellt werden. Der häufigste Fall ist eine Darstellung als duale Zahl mit sieben oder acht Bits. Wir kommen so zur Codetafel. In der linken Spalte stehen die Zeichen, in der rechten die Bits oder umgekehrt. Mit diesem Schritt ist die rechnerinterne Darstellung der Zeichen festgelegt, aber noch nicht deren Aussehen auf Schirm oder Papier, die Glyphen. Zu Zeiten, als Bits noch knapp und teuer waren, haben die Nordamerikaner eine Codetafel aufgestellt, in der die ihnen bekannten Buchstaben, Ziffern und Satzzeichen zuzüglich einiger Steueranweisungen wie Zeilen- und Seitenvorschub mit sieben Bits dargestellt werden. Das war Sparsamkeit am falschen Platz, aber schon ein Fortschritt gegenüber dem Fernschreib-Alphabet, das nur fünf Bits oder Löcher auf dem Lochstreifen vorsah. Mit sieben Bits unterscheide ich 27 = 128 Zeichen, nummeriert von 0 bis 127. Diese Codetafel ist unter dem Namen American Standard Code for Information Interchange (ASCII) weit verbreitet und seit 1963 in ISO 646 genormt, gleichzeitig auch als CCITT-Empfehlung V.3. Um Missverständnisse auszuschließen, heißt sie ausführlich 7-bit-US-ASCII. Jeder Rechner kennt sie. Die ersten 32 Zeichen der ASCII-Tafel dienen der Steuerung der Ausgabegeräte, es sind unsichtbare Zeichen. Ein Beispiel für Steuerzeichen ist das ASCII-Zeichen Nr. 12, Form Feed, das einen Drucker zum Einziehen eines Blattes Papier veranlasst. Auf der Tastatur werden sie entweder in Form ihrer Nummer oder mit gleichzeitig gedrückter-Taste erzeugt. Die Ziffern 0 bis 9 tragen die Nummern 48 bis 57, die Großbuchstaben die Nummern 65 bis 90. Die Kleinbuchstaben haben um 32 höhere Nummern als die zugehörigen Großbuchstaben, was das Umrechnen erleichtert. Der Rest sind Satzzeichen. Im Anhang ist die ASCII-Tafel wiedergegeben. Das Kommando: joe@debian:~$ man ascii bringt sie auf den Schirm. Textausgabegeräte wie Bildschirme oder Drucker erhalten vom Rechner die ASCII-Nummer eines Zeichens und setzen diese mittels eingebauter Software in das entsprechende Zeichen um. So wird beispielsweise die ASCII-Nr. 100 in den Buchstaben d umgesetzt. Die Ausgabe der Zahl 100 als Zeichenkette erfordert das Abschicken der ASCII-Nr. 49, 48, 48. Die US-ASCII-Tafel enthält nicht die deutschen Umlaute und andere europäische Absonderlichkeiten. Es gibt einen Ausweg aus dieser Klemme, leider sogar mehrere. Bleibt man bei sieben Bits, muss man einige nicht unbedingt benötigte US-ASCII-Zeichen durch nationale Sonderzeichen ersetzen. Für deutsche Zeichen ist eine Ersetzung gemäß DIN 66003 üblich, siehe Anhang 20.3 German ASCII auf Seite 1059. German ASCII ist als Variante Nr. 21 in ISO 646 registriert. Für Frankreich oder Schweden lautet die Ersetzung anders, siehe die deutsche Wikipedia unter dem Begriff ISO 646. Im Ausgabegerät, das die Umsetzung der ASCII-Nummern in Zeichen vornimmt, liegt eine solche Ersatztafel. Deshalb kann ein entsprechend ausgestatteter Bildschirm oder Drucker dieselbe Textdatei einmal mit amerikanischen ASCIIZeichen ausgeben, ein andermal mit deutschen ASCII-Zeichen. Im ersten Fall erscheinen geschweifte und eckige Klammern, im zweiten stattdessen Umlaute. Wer-
2.12 Editoren
173
den bei Ein- und Ausgabe unterschiedliche Codetafeln verwendet, gibt es Zeichensalat. Andersherum gesagt: Wenn ich einen Text ausgebe, muss ich die Codetafel der Eingabe kennen und verwenden. Spendiert man ein Bit mehr, so lassen sich 28 = 256 Zeichen darstellen. Das ist der bessere Weg. Die Firma IBM hat schon früh bei größeren Anlagen den Extended Binary Coded Decimal Interchange Code (EBCDIC) mit acht Bits verwendet, der nirgends mit ASCII übereinstimmt. Hätte sich diese Codetafel statt ASCII durchgesetzt, wäre uns Europäern einige Mühe erspart geblieben. Eine globale Lösung wäre es aber auch nicht gewesen. Bei ihren PCs schließlich wollte IBM außer nationalen Sonderzeichen auch einige Halbgrafikzeichen wie Mondgesichter, Herzchen, Noten und Linien unterbringen und schuf einen weiteren Zeichensatz namens IBM-PC, der in seinem Kern mit USASCII übereinstimmt, ansonsten aber weder mit EBCDIC noch mit Latin-irgendwas. Die internationale Normen-Organisation ISO hat mehrere 8-bit-Zeichensätze festgelegt, von denen einer unter dem Namen Latin-1 nach ISO-8859-1 Verbreitung gewonnen hat, vor allem in weltweiten Netzdiensten. Seine untere Hälfte ist mit USASCII identisch, die obere enthält die Sonderzeichen west- und mitteleuropäischer Sprachen. Der Aufruf: joe@debian:~$ man iso_8859_1 zeigt die Tafel. Polnische und tschechische Sonderzeichen sind in Latin-2 nach ISO 8859-2 enthalten. Die Zeichensätze Latin-1 bis 4 sind ebenfalls im Standard ECMA-94 beschrieben (ECMA = European Computer Manufacturers Association). Kyrillische Zeichen sind in ISO 8859-5, griechische in ISO 8859-7 festgelegt (logischerweise nicht als Latin-* bezeichnet; Latin-5 ist Türkisch nach ISO 8859-9). ISO 8859-15 oder Latin-9 entspricht Latin-1 zuzüglich des Eurosymbols und weiterer Kleinigkeiten. Die Latin-Zeichensätze enthalten außer dem gewohnten Zwischenraum- oder Leerzeichen (space) ein in Textverarbeitungen oft benötigtes Zeichen für einen Zwischenraum, bei dem kein Zeilenumbruch erfolgen darf (Latin-1 Nr. 160, no-break space). Im Satzsystem LATEX wird hierfür die Tilde verwendet, in HTML die Umschreibung (entity) . Dieses Zeichen kommt beispielsweise zwischen Zahl und Maßeinheit oder innerhalb eines Namens vor. Auch wenn die Ein- und Ausgabegeräte 8-bit-Zeichensätze kennen, ist noch nicht sicher, dass man die Sonderzeichen benutzen darf. Die Programme müssen ebenfalls mitspielen. Der hergebrachte vi-Editor, die curses-Bibliothek für Bildschirmfunktionen und einige Email-Programme verarbeiten nur 7-bit-Zeichen. Erst jüngere Versionen von Linux/UNIX mit Native Language Support unterstützen 8bit-Zeichensätze voll. Textverarbeitende Software, die 8-bit-Zeichensätze verträgt, wird als 8-bit-clean bezeichnet. Bei Textübertragungen zwischen Rechnern (insbesondere Email) ist Misstrauen angebracht. Die Konsequenz heißt in kritischen Fällen Beschränkung auf 7-bit-US-ASCII, das funktioniert immer und überall. Das Problem verliert jedoch an Bedeutung. Wenn wir das Abendland verlassen, stellen wir fest, dass es drei Gruppen von Schriften gibt:
174
• • •
2 Linux/UNIX
alphabetische Schriften (E: alphabetic scripts): Lateinisch, Griechisch, Kyrillisch, Arabisch, Hebräisch, Devanagari mit jeweils wenigen hundert Zeichen, Silbenschriften (E: syllabic scripts): Hiragana, Hangul, Thai, mit jeweils wenigen tausend Zeichen, Begriffsschriften (E: ideographic scripts): Chinesisch, Japanisch, Koreanisch, Vietnamesisch (CJKV), mit etwa 50000 Zeichen im Chinesischen.
Nicht alle Schriften werden – wie wir es gewohnt sind – von links nach rechts geschrieben und gelesen. Texte, die eine Mischung aus rechts- und linksläufigen Schriften enthalten, fordern die Software heraus. Mehrere Einrichtungen bemühen sich, diese Vielfalt unter einen Hut zu bringen: •
•
• • •
die International Standardization Organization (ISO), die Internationale NormenOrganisation mit Sitz in Genf, an der auch das Deutsche Institut für Normung (DIN) beteiligt ist, http://www.iso.org/ bzw. http://www2.din. de/, die International Electrotechnical Commission (IEC), an der die Deutsche Kommission Elektrotechnik Elektronik Informationstechnik im DIN + VDE beteiligt ist, http://www.iec.ch/ bzw. http://www.dke.de/, das Unicode Consortium, ein Zusammenschluss führender Hersteller und Institutionen sowie von Einzelpersonen, http://www.unicode.org/, das World Wide Web Consortium (W3C), zu erreichen unter http://www. w3.org/International/, das Internet in Form einiger Requests For Comments.
Eine große, gemeinsame Richtung ist erkennbar, wenn auch im Einzelnen die unterschiedlichen Bezeichnungen und Zuständigkeiten verwirren. Tab. 2.5: Darstellung des Zeichens A in verschiedenen Standards Standard
dual dez
ASCII (7 bit) 1000001 ISO 8859-1 (8 bit), UTF-8 01000001 UTF-16, UCS-2 (16 bit) 00000000 01000001 UTF-32, UCS-4 (32 bit) 00000000 00000000 00000000 01000001
65 65 65 65
ISO und IEC haben mit der Norm ISO/IEC 10646 einen Zeichensatz (Zeichen, Namen und Positionsnummer) festgelegt, der die Zeichen aller historischen, gegenwärtigen und zukünftigen Schriftsprachen der Erde berücksichtigt. Dieser Zeichensatz heißt Universal Character Set (UCS), ausführlich Universal Multiple Octet Coded Character Set. Unabhängig von der ISO arbeitet das Unicode Consortium seit 1991 an dem gleichen Ziel und hat einen Zeichensatz namens Unicode geschaffen. Glücklicherweise haben beide Einrichtungen zusammengefunden. Im Jahr 2003 erschien Unicode in Version 4, die der Norm ISO/IEC 10646:2003 folgt. Der einheitliche Zeichensatz reicht für mehr als eine Million Zeichen. Die Standards von ISO/IEC
2.12 Editoren
175
und Unicode unterscheiden sich in ergänzenden Abschnitten, die teilweise verschiedene Themen aufgreifen. Geht es nur um den Zeichensatz, bedeuten Unicode, UCS und ISO/IEC 10646 dasselbe. In den untersten 256 Zeichen ist Unicode mit Latin-1 identisch, in den untersten 128 Zeichen folglich mit US-ASCII. Die nächste Frage betrifft die Umsetzung der Zeichen oder ihrer Nummern in Bits und Bytes. Nahe liegt eine Darstellung durch 2 oder 4 Bytes, wobei das höchstwertige Byte zuerst übertragen wird (bigendian), wenn nicht anders bestimmt (UTF = Unicode Transformation Format): •
•
UCS-4 (UTF-32) verwendet eine feste Anzahl von 32 Bits (4 Bytes) und codiert damit bis zu 232 = 4294967296 Zeichen (Positionen). Der Bedarf an Speicherplatz ist maximal. UCS-2 (UTF-16) ist ein Kompromiss, der für die häufigeren Zeichen 2 Bytes (65 536 Positionen) und für die selteneren ein Paar von UCS-2-Codes (4 Bytes) verwendet. Von Java und MS Windows benutzt.
Die Linux/UNIX-Familie bekommt damit ein Problem. In UCS-2- oder UCS-4codierten Zeichenketten treten häufig Nullbytes (00000000) auf, siehe Tabelle 2.5. Diese haben in der Programmiersprache C und damit auch in Linux/UNIX eine besondere Bedeutung: Sie beenden eine Zeichenkette. Außerdem kennen die herkömmlichen Linux/UNIX-Werkzeuge nur Zeichen mit der Länge von einem Byte. Wir brauchen eine andere Codierung des Zeichensatzes. Die Lösung des Linux/UNIX-Problems – ausgedacht von K ENNETH T HOMP SON mit Unterstützung durch ROB P IKE, beide UNIX-Urgestein – besteht in UTF-8, das 1 bis 6 Bytes verwendet und alle UCS-Zeichen codiert. Der Bedarf an Speicherplatz ist minimal, dafür muss mehr gerechnet werden. UTF-8 codiert nach folgender Vorschrift: • •
•
•
• •
Die US-ASCII-Zeichen Nr. 0 bis 127 (hex 00 bis 7F) bleiben, wie sie sind: ein Byte, höchstwertiges Bit null. Alle Zeichen >127 werden als Folgen mehrerer Bytes codiert, wobei in jedem Byte das höchstwertige Bit gesetzt ist. Somit tritt kein ASCII-Byte als Teil eines Zeichens >127 auf. Das erste Byte einer Bytefolge, die ein Zeichen >127 darstellt, beginnt mit so vielen Einsen, wie die Folge Bytes umfasst, gefolgt von einer Null, und liegt damit im Bereich von 192 bis 253 (hex C0 bis FD). Die restlichen Bits tragen zur Codierung der Positionsnummer bei. Die weiteren Bytes der Folge beginnen stets mit 10, die restlichen Bits tragen zur Codierung der Positionsnummer bei. Die Bytes liegen damit im Bereich von 128 bis 191 (hex 80 bis BF). Die Bytes 254 und 255 (hex FE und FF) werden nicht verwendet. Jedes Zeichen wird durch seine kürzest mögliche Bytefolge codiert, die Codierung ist umkehrbar eindeutig.
Die Folgen können bis zu sechs Bytes lang sein; die 65000 häufiger vorkommenden Zeichen erfordern jedoch nur bis zu drei Bytes. UFT-8 ist im Web verbreitet und
176
2 Linux/UNIX
gewinnt in der Linux/UNIX-Welt an Boden. Ein Beispiel: Unser Eszett (ß) hat im Latin-1-Zeichensatz die Nr. 223 und damit auch in UCS oder Unicode. Der Aufruf: joe@debian:~$ unicode -d 223 liefert als Ergebnis: U+00DF LATIN SMALL LETTER SHARP S UTF-8: c3 9f UTF-16BE: 00df Decimal: ß SS (SS) Uppercase: U+00DF Category: Ll (Letter, Lowercase) Bidi: L (Left-to-Right) was Folgendes bedeutet: •
• • •
Das Zeichen mit der hexadezimal ausgedrückten Position U+00DF (dezimal 223) in Unicode trägt den Namen LATIN SMALL LETTER SHARP S, wird in UTF-8 durch hexadezimal c3 9f, in bigendian UTF-16 durch hexadezimal 00df dargestellt. Der zugehörige Großbuchstabe (den es nicht gibt) ist identisch mit dem Kleinbuchstaben. Das Zeichen fällt in die Kategorie der Kleinbuchstaben. Das Zeichen gehört zu den Schriften, die von links nach rechts geschrieben werden.
Probieren Sie obige Eingabe mit den Nr. 167, 188, 216, 555, 1648, 4711 und 12345 aus. Die UTF-8-Darstellung des Eszetts liest sich dual: c3 9f = 11000011 10011111 = 110 00011
10 011111
Von links gelesen ergibt die Anzahl der Einsen bis zur ersten Null die Anzahl der zum Zeichen gehörenden Bytes, hier zwei. Vom ersten Byte sind dann die ab der dritten Eins stehenden Stellen zu nehmen, hier 11. Vom zweiten Byte sind die beiden führenden Bits zu streichen, der Rest ist an die 11 vom ersten Byte anzuhängen, sodass sich 11011111 = 223 dezimal ergibt. Bei solchen Rechnungen ist der Zahlenbasiskonverter gbase aus dem gleichnamigen Debian-Paket hilfreich. Wir haben nun einen globalen Zeichensatz (UCS, Unicode) samt einer Codierung (UTF-8), die ins Konzept von Linux/UNIX passt. Jetzt muss noch die Software mitspielen. Werkzeuge wie cat lesen Bytes ein und geben Bytes aus, ohne auf ihre Bedeutung zu achten. Die brauchen nichts von UTF-8 zu verstehen. Aber überall dort, wo die Länge von Zeichenketten gefragt ist, müssen die Funktionen und Programme jetzt aufpassen. Die alte Gleichheit Byte = Zeichen = Schritt auf dem Bildschirm gilt nicht mehr. Ein Zeichen kann aus mehreren Bytes bestehen und – bei den CJKV-Sprachen – doppelt breit sein. Bestimmte Zeichen wie selbständige Akzente veranlassen keinen Schritt auf dem Bildschirm, haben also die Breite null. Betroffen sind Programme wie ls, wc und die Texteditoren. Da der Benutzer keinen Einfluss auf seine Werkzeuge hat, bleibt nur die Hoffnung, dass die Textwerkzeuge unter Debian GNU/Linux zunehmend UTF-8 beherrschen. Auf der Webseite zum
2.12 Editoren
177
Debian-Paket vim aus etch ist von Unicode die Rede; der aktuelle Emacs murmelt etwas von some Unicode support. Es gibt auch Editoren, die speziell für UTF-8 und bidirektionale Schreibweise entwickelt worden sind, aber das führt hier zu weit. Zusammenfassend noch einmal die drei in der Linux/UNIX-Welt gebräuchlichsten Zeichensätze und Codierungen: • •
•
Alt, bewährt, verbreitet, aber beschränkt in seinen Möglichkeiten: 7-bit-ASCII nach ISO 646, verbreitet, aber nur für das Abendland ausreichend: ISO/IEC 8859 mit 8 Bits, daraus für West- und Mitteleuropa ISO-8859-1, auch Latin-1 oder IBM Codepage 819 genannt. UCS bzw. Unicode mit der Codierung UTF-8, nach ISO/IEC 10646 und Unicode Version 4, ein bis sechs Bytes, zukunftssicher und zunehmend unterstützt.
Wer sich eingehender mit Unicode und UTF-8 beschäftigen möchte, findet einen Einstieg bei: • • • • • •
http://www.unicode.org/Public/ sowie http://www.unicode. org/faq/, RFC 3629 UTF-8, a transformation format of ISO 10646. B RUNO H AIBLE, The Unicode HOWTO (2001), http://www.tldp.org/ HOWTO/Unicode-HOWTO.html, M ARKUS K UHN, UTF-8 and Unicode FAQ for Unix/Linux, http://www.cl. ac.uk/~mgk25/unicode.html, ROMAN C ZYBORRA, Unicode Transformation Formats: UTF-8 & Co., http: //czyborra.com/utf/, A LAN W OOD, Unicode and Multilingual Support in HTML, Fonts, Web Browsers and Other Applications, http://www.alanwood.net/unicode/,
Die Standards gibt es gegen Entgelt bei den jeweiligen Organisationen. Zu Fonts17 , also den Darstellungen der Zeichen auf Bildschirm oder Papier, findet sich bei TLDP ein HOWTO Optimal Use of Fonts on Linux (2007) von AVI A LKALAY und weiteren Autoren. Die hervorragende comp.fonts FAQ von N ORMAN WALSH (1996, http: //nwalsh.com/) ist leider am Verschwinden. 2.12.3 Die vi-Familie Der Standard-Texteditor auf allen Linux/UNIX-Systemen ist der vi (visual editor). Deshalb sind Grundkenntnisse des vi hilfreich, auch wenn Sie zur Arbeit einen anderen Editor bevorzugen. Ein weiterer Vorteil des vi besteht darin, dass er keine grafische Oberfläche braucht. Sie können ihn auch dann benutzen, wenn Sie sich über das Netz auf einem anderen System angemeldet haben oder wenn Sie die Konfiguration Ihrer grafischen Oberfläche reparieren müssen. Der Original-vi steht unter 17
Ein Font ist eine Sammlung von Schriftzeichen, ursprünglich der Inhalt eines Setzkastens, allenfalls noch eine Gemeinde im Kanton Fribourg (http://www.font.ch/). Ein Fonds – französisch auszusprechen – hat etwas mit Geld zu tun. Einen Fond – ebenfalls französischen Ursprungs – gibt es im Auto, auf der Bühne und in der Küche.
178
2 Linux/UNIX
Debian nicht zur Verfügung, dafür aber mehrere vi-ähnliche Editoren wie vim (vi improved) oder nvi (new vi). Diese bieten zusätzliche Eigenschaften, die dem ursprünglichen vi fehlen. Sie verstehen jedoch alle die im Folgenden gezeigten Kommandos. Welche vi-Variante nach Eingabe des Kommandos vi tatsächlich aufgerufen wird, wird mit dem Alternativen-System festgelegt. Unter etch für vi vorgegeben ist /usr/bin/nvi. Im Abschnitt 2.8.8 Verschieben und Umbenennen auf Seite 138 hatten wir ein Unterverzeichnis texte angelegt und eine Textdatei namens GPL-Komm dorthin verschoben. Wir wechseln in das Unterverzeichnis und geben folgendes Kommando ein: joe@debian:~/texte$ vi GPL-Komm Achten Sie auf Groß- und Kleinschreibung und benutzen Sie so bald wie möglich die-Taste zwecks Vervollständigung der Wörter. Dann probieren wir verschiedene Editierkommandos aus. Navigation im Text Sie sehen nun den Inhalt der Datei auf dem Bildschirm. Ähnlich wie bei less sollten Sie in der Lage sein, mit den Tasten <Page Up> und <Page Down> im Text vor- und zurückzublättern. Genauso können Sie die Pfeiltasten verwenden, um die Einfügemarke (den Cursor) im Text zu bewegen und zeilenweise vorwärts und rückwärts zu gehen. Weil die Tasten jedoch nicht überall zu Verfügung stehen oder nicht richtig konfiguriert sind, dienen zur Bewegung des Cursors zusätzlich folgende Kommandos: <j> <0> <$> <7>
Cursor nach links Cursor nach rechts Cursor nach unten Cursor nach oben (null) Cursor zum Zeilenanfang Cursor zum Zeilenende Cursor zur siebten Zeile Cursor zum Textende
Während der Eingabe von Text (im Eingabemodus) können Sie keines der vorstehenden Kommandos zur Navigation im Text benutzen, da die Tasten als normale Texteingabe aufgefasst werden. Um die Eingabe zu beenden und in den Kommandomodus zu wechseln, muss die Taste <esc> betätigt werden. Wenn sich Ihr vi verrückt gebärdet, schauen Sie einmal auf die Caps-Lock-Taste. Die verleiht vielen Tastenkommandos eine völlig andere Bedeutung. Ein Beispiel liefert die viel benutzte Taste <j>. Suchen Genauso wie in less können Sie mit dem vi durch Eingabe der Kommandofolge >musternach bestimmten Textmustern suchen, wobei muster durch
2.12 Editoren
179
die Zeichenfolge ersetzt werden muss, nach der gesucht werden soll. Weiterhin ist es möglich, durch die Kommandofolge >nach dem gleichen Text wie bei der vorhergehenden Suche zu suchen. Hierzu kann – wie bei less – auch das Kommando benutzt werden. Das Suchmuster ist ein regulärer Ausdruck, siehe Abschnitt 2.10.1 Reguläre Ausdrücke auf Seite 157. Einfügen Zum Einfügen von Text dienen folgende Kommandos:
<esc>
Einfügen von Text vor dem Cursor (insert) Einfügen von Text hinter dem Cursor (append) Erzeugen einer neuen (leeren) Zeile unterhalb der Zeile, in der sich der Cursor befindet, und Einfügen von Text in diese Zeile (open) Erzeugen einer neuen (leeren) Zeile oberhalb der Zeile, in der sich der Cursor befindet, und Einfügen von Text in diese Zeile (Open) Beenden der Eingabe (Umschalten in Kommandomodus), kann mehrmals hintereinander gegeben werden
Nachdem eines der oben aufgeführten Kommandos benutzt wurde, kann mit der Tastatur Text eingegeben werden. Falsche Eingaben werden mit der Taste(Backspace) rückgängig gemacht. Wenn Sie während der Texteingabe die Taste drücken, wird eine neue Zeile unterhalb der aktuellen Zeile erzeugt, das heißt ein Zeilenwechsel geschrieben. Mit <esc> beenden Sie die Eingabe und gelangen in den Befehlsmodus. Löschen Zum Löschen von Text stehen folgende Kommandos zur Verfügung: <x> <w>
Löschen des Zeichens, auf dem sich der Cursor befindet Löschen des Wortes, auf dessen Anfang sich der Cursor befindet Löschen der Zeile, auf der sich der Cursor befindet
Das Löschen funktioniert selbstverständlich nur im Kommandomodus, andernfalls werden die Buchstaben geschrieben. Verschieben Mit folgenden Kommandos werden Textzeilen verschoben:
(yank) Die Zeile, auf der sich der Cursor befindet, wird in einen Zwischenspeicher kopiert. Falls sich vorher anderer Text im Zwischenspeicher befunden hat, wird dieser überschrieben
180
2 Linux/UNIX
<3>