Die Linux-Kommandozeile kann weitaus mehr als die gute alte DOS-command.com. Doch viele ihrer Schätze sind schwer zu finden.
Dass das Eintippen eines Kommandos am X-Terminal oder in einer Konsole oft schnellere Erfolge zeitigt als eine Latte Mausklicks in einer GUI-Applikation, ist eine Binsenweisheit. Mit den Pfeiltasten lassen sich bereits eingegebene Kommandos aus der History, dem Lager für gebrauchte Kommandos, zurückholen, bearbeiten und per [Return] erneut auf die Reise schicken. Auch die [Tab]-Taste zum Ergänzen von Befehlen und Dateinamen in der bash zählt noch zur Allgemeinbildung einer Linuxerin.
The Answer Girl
Dass der Computeralltag auch unter Linux des Öfteren für Überraschungen gut ist, ist eher eine Binsenweisheit: Immer wieder funktionieren Dinge nicht oder nicht so, wie eigentlich angenommen. Das Answer-Girl im LinuxUser zeigt, wie man mit solchen Problemchen elegant fertig wird.
Doch dann hört es meistens schon auf. Spätestens, wer fünf Minuten lang mit [Pfeil hoch] und [Pfeil runter] nach einem älteren Kommando gegraben hat, das schneller neu eingetippt gewesen wäre, fragt sich, ob die Shell nicht doch noch ein paar Arbeitserleichterungen mehr bietet.
Von diesem Gedanken getrieben, entdeckte das Answer Girl in den Heften 12/2000 [1] und 04/2001 [2] den Event Designator!#, der einfach das wiederholt, was man bereits in der aktuellen Kommandozeile eingegeben hat. Mit Modifizierern wie :1 beschränkt man die Auswahl auf das zweite Wort dieses Ausdrucks (die Zählung beginnt bei Null), sodass ein
echo hallo !#:1
den Befehl echo hallo hallo ausführt. Selbst wenn man noch ein :p anhängt und das entstehende Kommando damit lediglich ausgibt (“print”), aber nicht tatsächlich ablaufen lässt, ist das eher nur nett zu wissen. Denn in der Praxis hat der/die weniger Geübte das Kommando meist komplett eingetippt, bevor er/sie sich der recht kruden Syntax erinnert.
Ausrufezeichen mit großer Wirkung
Wer jedoch dem einen oder anderen Guru schon mal beim Tippen über die Schulter geschaut hat, dem/der ist womöglich aufgefallen, dass zumindest das Ausrufezeichen recht oft über die Kommandozeile huscht, und zwar meist in der Form
!Befehlsanfang
Ein !man ruft so zum Beispiel die Manpage noch einmal auf, in der man zuletzt gestöbert hat, während !ssh den zuletzt eingegebenen ssh-Befehl erneut zur Ausführung bringt. Sie trauen der Sache nicht so recht und wollen lieber vorher wissen, welches Kommando zum Zuge käme? Dann lassen Sie uns doch einfach ausprobieren, was passiert, wenn wir den bereits erwähnten Modifizierer :p mit der Ausrufezeichensuche verknüpfen:
pjung@chekov:~$ !ssh :p ssh bashir :p pjung@bashir's password:
Das war wohl falsch: Da der Zielrechner bashir nach dem Passwort fragt, hat :p anscheinend nicht verhindert, dass das letzte ssh-Kommando, ssh bashir, ausgeführt wurde. Doch halt! Wieso steht in der ersten Antwortzeile der Shell, dass sie nun das Kommando ssh bashir :p ausführt? Da haben wir die betroffene Kommandozeile wohl schlichtweg um die Zeichenkette “ :p” ergänzt. Das war zwar nicht das, was wir wollten, aber gut zu wissen, dass es geht.
[Strg-c] jedenfalls sorgt dafür, dass das fälschlich aufgerufene Kommando abgebrochen wird. Doch wo war der Fehler? Ein einfaches Leerzeichen, denn
pjung@chekov:~$ !ssh:p ssh bashir :p
zeigt tatsächlich einfach nur an, dass das zuletzt abgeschickte (aus unserem Fehlversuch herrührende) ssh-Kommando ssh bashir :p hieß. Das :p brauchen wir aber nicht. Wer jetzt nicht ohnehin entnervt ssh bashir tippt, sondern lieber ein wenig puzzelt, lässt sich mit
pjung@chekov:~$ !ssh:0-1:p ssh bashir
das Kommando anzeigen, das entsteht, wenn wir aus dem zuletzt benutzten ssh-Befehl das nullte (ssh) und das erste Wort (bashir) abgreifen.
!ssh:0-1
riefe diese Kommandozeile jetzt auf.
Einblicke in die Geschichte
Leider speichert die Bash die Ausrufezeichen-Variationen der Kommandos so nicht in ihrer History ab. Stattdessen findet man mit den Pfeiltasten oder dem Shell-Builtin (vgl. Kasten 1) history nur die bereits von der Shell ersetzte Version wieder (die history-Funktion interpretiert ein numerisches Argument als die Anzahl der aufzulistenden letzten Kommandos):
pjung@chekov:~$ history 3 955 date 956 ssh bashir :p 957 ssh bashir
Die davon ausgegebenen Kommandozeilen wiederzuverwerten, sollte anhand der Nummern kein Problem sein. Wenn sich !# auf die aktuelle Befehlszeile, !! hingegen auf die letzte bezieht, liegt es nahe, doch einfach einmal
pjung@chekov:~$ !955 date Fri Dec 21 14:45:20 CET 2001
auszuprobieren – und siehe da, es funktioniert. Der Event-Designator muss dabei nicht einmal an erster Stelle stehen: ping !956:1 etwa klaut sich einfach aus dem 956. Kommando in der History das erste Argument heraus und führt damit den Befehl ping bashir aus.
Kasten 1: Eingebaut@KL:Wenn die Anwenderin auf der Kommandozeile
drückt, beginnt die Arbeit für die Shell. Sie schaut nach, ob sie in dem, was eingetippt wurde, etwas ersetzen oder ergänzen muss (die Ausrufezeichen-Konstrukte sind ein gutes Beispiel). Erst nach dieser Vorarbeit wird sie den Kernel mit der Ausführung entsprechener Prozesse beauftragen. Beim ersten Wort der bearbeiteten Kommandozeile handelt es sich um das Kommando, das gestartet werden soll.
Die Bash und verwandte Shells schauen zunächst nach, ob es einen Alias dieses Namens gibt. Wenn nein, überprüfen sie, ob sie es mit einer Shell-Funktion zu tun haben. Das können in der Shell selbst implementierte Funktionen, die Shell-Builtins, sein oder aber auch selbst definierte. Im Gegensatz zu Aliasen können Funktionen nicht nur Argumente mit auf den Weg bekommen, sondern diese auch bearbeiten. Erst wenn die Shell weder Alias noch Funktion findet, kommt ein externes Programm zum Zuge. Das Bash-Builtin type gibt der Anwenderin die Möglichkeit, herauszufinden, ob ein Kommando tatsächlich ein eigenständiges Binary oder “nur” ein in der Shell eingebauter Befehl ist. Mit überraschenden Erkenntnissen, wie …
pjung@chekov:~$ type cd cd is a shell builtin
… die Probe auf’s Exempel zeigt: Das Kommando zum Verzeichniswechseln (“change directory”) ist gar keine ausführbare Datei, sondern ein Shell-Builtin, das wir mit einer selbstdefinierten Shell-Funktion überschreiben können:
pjung@chekov:~$ cd(){ echo Du willst nach $1 wechseln? Nichts gibts… ; }
Wie in anderen Programmiersprachen auch folgt nach dem Funktionsnamen ein rundes Klammernpaar als Kennzeichen dafür, dass es sich um eine Funktion handelt. Allerdings kann diese Klammer bei der Bash selbst dann leer bleiben, wenn die Funktion mit (Kommandozeilen-)Argumenten hantiert.
Die geschweiften Klammern enthalten die Kommandos, die beim Funktionsaufruf auszuführen sind. Wichtig dabei: Jedes Kommando muss mit Semikolon abgeschlossen werden, und vergessen Sie das Leerzeichen nach { nicht. Mit $1 greifen wir auf das erste Kommandozeilenargument von cd zurück. Wenn wir jetzt den Wunsch zum Wechseln des Verzeichnisses verspüren, stellt sich der Rechner stur:
pjung@checkov:~$ cd /mnt/cdrom Du willst nach /mnt/cdrom wechseln? Nichts gibts…
Zum Vergleich: Mit einem cd-Alias ist die Auswertung der Parametervariablen 1 nicht möglich:
pjung@chekov:~$ alias cd="echo Du willst nach $1 wechseln? Nichts gibts…" pjung@chekov:~$ cd /tmp Du willst nach wechseln? Nichts gibts… /tmp
Hierbei nimmt die Shell den gesamten “cd /tmp“-Befehl und tut nichts anderes, als cd mit echo Du willst nach $1 wechseln? Nichts gibts… zu ersetzen. Ausgeführt wird echo Du willst nach $1 wechseln? Nichts gibts… /tmp. Mit unalias cd heben wir den Alias wieder auf. Wenn wir jetzt das cd-Kommando eingeben, greift die Shell wieder auf die von uns definierte Funktion zurück. Um die loszuwerden und wieder mit dem Builtin ordentlich das Verzeichnis wechseln zu können, gibt es glücklicherweise ein weiteres Builtin namens unset: unset cd macht dem Spuk einer renitenten Shell ein Ende.
Fast alles wie im Emacs
Eine unendliche Spielwiese, doch einfacher tut man sich in der Regel, wenn man – wie mit den Pfeiltasten – ein altes Kommando auf die Befehlszeile bekommt und darin nach Herzenslust herumeditieren kann. Dafür zuständig ist – man bash gibt Aufschluss – die Readline-Bibliothek, die wiederum dafür sorgt, dass Emacs-Benutzer(innen) bereits bekannte Emacs-Tastenkürzel zum Editieren der Kommandozeile benutzen können. (Der ebenfalls mögliche und in der aktuellen Shell mit set +o vi einschaltbare vi-Modus dagegen ist selbst bei Hardcore-Vi-Verfechter(inne)n kaum in Gebrauch.) Zu beachten gibt es dabei lediglich, dass nicht alles, was in einem Editor mit ausgedehnten Fähigkeiten möglich ist, auch für einen Zeilen-Editor, wie die Shell ihn mit der Kommandozeile bietet, sinnvoll ist. Der Emacs-Modus der Shell umfasst also nur einen winzigen Bruchteil der Möglichkeiten des Namensgebers.
Doch lassen Sie uns einiges ausprobieren. Da der Emacs mit [Strg-r] rückwärts sucht, sollte sich denn wohl auch in der Bash mit dieser Tastenkombination etwas anstellen lassen. Versuchen wir einmal, den ping-Befehl auf bashir aus der Beispiel-History zu kramen. Tatsächlich bringt ein [Strg-r]ba das Ergebnis
(reverse-i-search)`ba': man bash
…, das letzte eingetippte Kommando, das die Zeichenkette ba enthielt. Ein Druck auf die [Return]-Taste zum Abschicken dieses Befehls bietet sich an dieser Stelle nicht an, denn wir haben die gesuchte Kommandozeile noch gar nicht gefunden. Also vervollständigen wir unser bisheriges Suchmuster ba mit shi, und schon schlägt die Shell
(reverse-i-search)`bashi': ping bashir
vor. Sollte auch dies nicht unseren Wünschen entsprechen, hilft tatsächlich das Emacs-Abbruchkommando [Strg-x][Strg-c] (Abbrechen ohne Speichern) weiter. Doch wozu umständlich, wenn’s auch einfach geht: Ein einfaches [Strg-c] (bekannt als Tastenkommando zum Beenden nicht-renitenter Vordergrundprozesse) tut es hier ebenfalls.
Doch was tun, wenn das gefundene Kommando zwar größtenteils unseren Erwartungen entspricht, aber eben doch nicht so ganz? Ein (ebenfalls nicht ganz Emacs-konformes) [Esc] sorgt dafür, dass das aktuell gefundene Kommando zum Editieren in der Kommandozeile erscheint.
Der Zielrechner heißt nicht bashir, sondern bahsir? Im Emacs tauscht [Strg-t] zwei vertauschte Buchstaben aus. Also Cursor auf’s h in bashir gesetzt und [Strg-t] gedrückt, schon wechseln das h und das davorstehende s die Plätze.
Was mit Buchstaben geht, sollte auch mit ganzen Wörtern gehen. Hier kann man sich von der Faustregel leiten lassen, dass ähnliche Aktionen (manchmal) auch ähnliche Kürzel haben: Das t wie “tauschen” bleibt, doch statt [Strg] gilt es, [Alt] zu drücken. Steht der Cursor über bahsir, tauscht dieses Wort auf ein [Alt-t] hin mit seinem Vorgänger den Platz: Aus ping bahsir wird bahsir ping. Ein weiteres [Alt-t] tauscht die beiden auch wieder zurück. Zu beachten gibt es dabei lediglich, dass auch Bindestriche und Punkte hierbei als “Worttrenner” zählen: Hat man etwa den Namen einer Datei (zum Beispiel index.html) am Prompt eingegeben und kommt jetzt darauf, schlafmützigerweise den darauf anzuwendenden Befehl vergessen zu haben, kann man das vi (oder emacs oder less…) zwar dahinter schreiben…
pjung@chekov:~$ index.html vi
… und [Alt-t] drücken. Das Ergebnis ist jedoch nicht vi index.html, sondern
pjung@chekov:~$ index.vi html
Die Dateinamensendung, durch einen Punkt vom Basisnamen index getrennt, zählt als Wort und wird demzufolge gegen die Zeichenkette vi getauscht. Diese eindeutig missglückte Tauschaktion hätten wir gern rückgängig gemacht. Im Emacs geht das mit [Strg-x-u], und siehe da, auch die Bash stellt daraufhin wieder das alte index.html vi zur Schau.
Zweimal die Backspace-Taste sorgt jetzt zwar dafür, dass das vi am Ende wieder verschwindet, aber sobald ganze auszuradierende Wörter etwas länger werden, erspart ein Tastenkürzel zum Entfernen des vor dem Cursor stehenden Worts den Handgelenken einige Belastung. Also machen wir gleich Nägel mit Köpfen und begeben uns auf die Suche nach der entsprechenden Tastenkombination.
Alles meta
Nur – wonach suchen wir eigentlich? So etwas wie eine Tastenbelegungstabelle enthält die bash-Manpage leider nicht. Doch da war doch was, Readline-Bibliothek …, wenn die für die Manipulationsmöglichkeiten der Kommandozeile zuständig ist, sollte doch unter diesem Thema etwas zu finden sein. Tatsächlich gibt es einen Abschnitt READLINE, der netterweise auch gleich erklärt, warum es so schwierig ist, als unbedarfter User dieser Manpage nach Tastenkürzeln zu fahnden: Die Dokumentation verwendet für deren Angaben die Emacs-Syntax.

Abbildung 1: Der READLINE-Abschnitt der bash-Manpage (links) ist fast komplett aus der readline-Manpage abgekupfert
Das heißt, dass [Strg] mit C (wie die Beschriftung der entsprechenden englischen Taste [Ctrl]) gemeint ist, während M einen mysteriösen Meta-Key bezeichnet. Den gibt es jedoch auf PC-Tastaturen nicht. Je nach Vorkonfiguration des Rechners übernimmt die [Alt]- und/oder die in der Manpage erwähnte [Esc]-Taste seine Funktion. Und tatsächlich: Statt [Alt-t] kann auch [Esc-t] zwei Wörter vertauschen.
Allerdings, so macht die Manpage im Anschluss klar, sind alle Angaben ohne Gewähr: Sollten dokumentierte Tastenkombinationen andere Effekte als die beschriebenen haben, dann liegt das vermutlich an individuellen Einstellungen in den Readline-Konfigurationsdateien: Wenn die Umgebungsvariable INPUTRC nichts anderes sagt, kommt das persönliche Konfigurationsfile ~/.inputrc zum Zuge. Zudem besteht die Möglichkeit einer in den Manpages einiger Distributionen nicht erwähnten globalen Konfigurationsdatei /etc/inputrc.
Umlaute sind Readline-Sache
Inputrc? Wer bei einer schlecht vorkonfigurierten Distribution schon einmal versucht hat, die Umlaute eines deutschen Keyboards in der Textkonsole zur Darstellung zu bewegen, bei dem/der sollte dieser Name ein Glöckchen läuten. Drei mysteriöse Zeilen (Listing 1) in der /etc/inputrc haben hier schon vielen weitergeholfen – doch wird erst jetzt klar, was sie bedeuten: Mit den darin gesetzten drei Variablen konfiguriert man die Readline-Bibliothek korrekt.
Listing 1
Umlaute in der Konsole funktionieren nur bei richtig gesetzten Readline-Variablen
set meta-flag on # Die nun angeschaltete Variable meta-flag sorgt dafür, dass # die Bash niemals das achte Bit eines Buchstabens abschneidet. # Umlaute lassen sich nämlich nur in 8-Bit-, nicht aber in # 7-Bit-ASCII darstellen. set output-meta on # 8-Bit-Zeichen werden jetzt richtig (und nicht als # komische Escape-Sequenzen) dargestellt. set convert-meta off # convert-meta ist standardmäßig angeschaltet und sorgt dann # dafür, dass 8-Bit-Zeichen in einen Escape-Character und # ein 7-Bit-ASCII-Zeichen konvertiert werden. Umlaute gehen # dabei natürlich kaputt, weshalb diese Option abzuschalten ist.
Doch zurück zur Sache: Das, was wir suchen, ist offensichtlich ein Readline-Kommando, das ein Wort rückwärts löscht. Zwar bietet der naheliegende Unterabschnitt Commands for Changing Text nichts passendes, doch bei Killing und Yanking (“Löschen und wieder einfügen”, wobei yank im wörtlichen (und bildlichen) Sinne aus einem ominösen Papierkorb, dem “kill ring” bereits gelöschte Strings wieder “herauszieht”) werden wir fündig:
backward-kill-word (M-Rubout)
Kill the word behind the cursor. […]
Wenn wir jetzt noch wüssten, was eine Rubout-Taste ist… Netterweise setzt uns gleich der erste Treffer einer Google-Suche nach Rubout key (Abbildung 2) davon in Kenntnis, dass es sich lediglich um einen anderen Namen für die [Backspace]-Taste handelt. Tatsächlich funktioniert [Esc-Backspace] wie gewünscht – nicht jedoch [Alt-Backspace], was schade ist, aber der Manpage recht gibt, die als Ersatz für die Meta-Taste nur [Esc], nicht aber das bei ordentlichen Emacs-Vorkonfigurationen auch mögliche [Alt] vorsieht.
Der ärgerliche vi-String aus der “index.html vi“-Beispielkommandozeile ist also weg – jetzt müssten wir nur auf schnellstem Wege an den Zeilenanfang zurück, um ihn dort mit C-y, also [Strg-y], wieder einzufügen (“yank”). Der passende Readline-Befehl steht im Abschnitt Commands for Moving und ist mit [Strg-a] leicht zu merken. Dass [Strg-e] den Cursor ans Zeilenende befördert, lernen wir genauso im Vorbeigehen, wie die nützliche Möglichkeit, mit M-b ein Wort vorwärts, mit M-f hingegen ein Wort rückwärts zu springen.
Jetzt fehlt eigentlich nur noch eine Übersicht, die alle vorbelegten Tastenkürzel auflistet und ihre Bedeutung dahinterschreibt. Die gibt es tatsächlich – aber nicht in allen Distributionen. Wer mit man readline eine eigene Manpage zu readline(3) findet, braucht nur in den Abschnitt namens DEFAULT KEY BINDINGS zu schauen. Doch ehe der Rest seinen Distributor verflucht, sei verraten: Dieser Abschnitt ist fast alles, was das readline-Manual dem Bash-Handbuch voraus hat. Wer hier wohl von wem abgeschrieben hat?
Glossar
-
Shell-Prompt
-
Das Bereitzeichen der Shell. Erst wenn eine Zeichenkette, die gern den User- und/oder Rechnernamen, aber auch das Arbeitsverzeichnis enthält und typischerweise auf $, > oder (bei root) auf # endet, auf der Kommandozeile zu sehen ist, nimmt die Shell Kommandos an. Selbstverständlich kann der Prompt individuell angepasst werden.
-
Shell
-
Der Kommandozeilen-Interpreter, der Benutzerbefehle für die Ausführung durch den Kernel vorbereitet und diesem übergibt. Vom Standpunkt eines Kommandozeilenusers umschließt die Shell den Kernel wie eine Muschel, daher der Name.
-
X-Terminal
-
Ein GUI-Programm, das eine Kommandozeile zur Verfügung stellt. Egal, ob sich das Programm xterm, konsole oder aterm nennt, läuft darin immer auch eine Shell.
-
bash
-
Die Standard-Shell unter Linux. Ihr Name, “Bourne Again Shell”, weist darauf hin, dass sie mit der traditionellen Bourne Shell sh kompatibel ist, aber zusätzlich eine Menge Funktionalität mitbringt, die jene nicht hat.
-
Emacs
-
Neben dem vi der zweite, auf fast jedem Unix-System installierte Standard-Texteditor. Wie vom vi auch, existieren verschiedene Implementationen dieses Editors, von denen die beliebteste wohl die GUI-Applikation xemacs ist. Wer sich einmal in die teils recht gewöhnungsbedürftige Bedienung eingearbeitet hat, bekommt ein äußerst vielfältiges, in der Programmiersprache Lisp erweiterbares Tool an die Hand, das dank verschiedener, in Emacs-Lisp geschriebener Module von der Programmierumgebung bis hin zum E-Mail- und News-Programm alle möglichen Einsatzgebiete abdeckt.
-
Vordergrundprozesse
-
Ruft man ein Kommando für sich auf der Kommandozeile auf, so bleibt diese Shell so lange blockiert, bis dieses Vordergrundkommando sich beendet. Bei Kommandozeilenbefehlen wie ls ist das normalerweise kein Problem, doch wer ein GUI-Programm aufrufen will, möchte die Shell für die Dauer seiner Benutzung nur ungern unbrauchbar sehen. Deshalb kann man Befehle in den Hintergrund schicken: Fügt man an den Befehl ein & an, blockiert er die aufrufende Shell nicht mehr.





