Shellkonfiguration – Übersicht
Wort löschen (definiert als Gruppe alphanumerischer Zeichen; ohne quoting)
Wort löschen (begrenzt durch einen nicht quoting-geschützten metacharacter)
vom Cursor bis zum Zeilenanfang / Zeilenende löschen
Pfadkomponenten löschen
Paste
ein Zeichen
ein Wort (definiert als Gruppe alphanumerischer Zeichen; ohne quoting)
ein Wort (begrenzt durch einen nicht quoting-geschützten metacharacter)
Anfang und Ende der Kommandozeile
Sprung zu einem bestimmten Zeichen
eine physische Bildschirmzeile nach oben / unten
Das Beherrschen der wichtigsten Funktionen zum Editieren der Kommandozeile ist nicht nur ein gigantischer Produktivitätsgewinn, sondern erspart einem auch die potentiell sehr nervtötenden Alternativen. Es ist vergleichbar mit dem Wechsel von nano zu vim (Readline hat sogar einen vim-Modus).
Strenggenommen...
Die Readline-Dokumentation benutzt nicht die Bezeichnung Alt, sondern Meta. Typischerweise ist Alt als Meta-Taste definiert. Alternativ kann man Esc nutzen (dann ist sogar egal, ob man die Esc-Taste noch gedrückt hält oder vor dem "zu meta-isierenden" Zeichen).
Der (mutmaßlich) besseren Lesbarkeit / Verständlichkeit halber wird im folgenden dennoch Alt verwendet.
Grundsätzlich ist die shell history immens nützlich; das gilt aber nicht für alle Varianten des Zugangs zu dieser Funktion für alle Nutzer. Zu den für mich wichtigsten Funktionen gehört die Bewegung durch die shell history mit den Pfeiltasten nach oben und nach unten (aus Sicht der Shell sind die Pfeiltasten normalerweise die Zeichenfolge Esc-[-A bzw. Esc-[-B):
ec:0 15:57:04 hl@notebook:~ start cmd: bind -p | grep -e previous-history -e next-history "\C-n": next-history "\eOB": next-history # Pfeiltaste nach unten (im Applikations-Modus) "\e[B": next-history # Pfeiltaste nach unten (im normalen Terminal-Modus) "\C-p": previous-history "\eOA": previous-history # Pfeiltaste nach oben (im Applikations-Modus) "\e[A": previous-history # Pfeiltaste nach oben (im normalen Terminal-Modus) ec:0 15:57:06 hl@notebook:~ start cmd:
Dieselbe Funktion kann also über unterschiedliche Tastatureingaben aufgerufen werden. Der obige Shell-Code zeigt, wie man herausfinden kann, wie eine Readline-Funktion heißt (wenn man weiß, über welche Tastatureingabe sie ausgelöst wird) und welche Tastatureingabe eine Readline-Funktion auslöst (wenn man weiß, wie sie heißt). Die Funktion für Pfeiltaste nach unten bekommt man mit bind -p | grep -F '"\e[B"'
Die Option -F
ist wichtig, weil sie dafür sorgt, dass das Argument als einfacher Text verstanden wird und nicht als regulärer Ausdruck. Wenn man nicht weiß, wie die Shell eine Taste (bzw. ein Zeichen) sieht, kann man sich das mit z.B. od
oder cat
anzeigen lassen. In diesem Fall bekommt man dann nicht die Ausgabe \e[B
, sondern ^[[B
, weil Esc vom Terminal als ^[
angezeigt wird.
Das bash-builtin history
zeigt die gesamte history an (und ermöglicht deren Manipulation). Mehrere Shell-Variablen steuern das Verhalten der history:
HISTCONTROL |
Durch Doppelpunkt getrennte Werte:
|
HISTFILE |
Die Datei, in die (ggf.) beim ordentlichen die aktuelle history geschrieben wird (mit oder ohne Zeitstempel, siehe HISTTIMEFORMAT ). Der Standardpfad ist ˜/.bash_history |
HISTSIZE |
Die Anzahl der Kommandozeilen, die (maximal) in der RAM history gespeichert werden. Standardmäßig 500. |
HISTFILESIZE |
Die Anzahl der Kommandozeilen, die (maximal) in der RAM history gespeichert werden. Standardmäßig 500 (der Wert von $HISTSIZE ). |
HISTIGNORE |
Durch Doppelpunkt getrennte globbing patterns. Kommandozeilen, die auf eins der patterns passen, werden der history nicht hinzugefügt. |
HISTTIMEFORMAT |
Wenn diese Variable gesetzt ist (auch wenn sie leer ist), werden in die history-Datei nicht nur die Kommandozeilen geschrieben, sondern auch die zugehörigen Zeitstempel (in der RAM-history werden sie immer gespeichert). |
hl@notebook:~ $ HISTTIMEFORMAT='%Y-%m-%d %H:%M:%S ' hl@notebook:~ $ history 3 2024-08-19 00:31:23 echo a 4 2024-08-19 00:31:25 echo b 5 2024-08-19 00:31:27 echo c
Wenn es mehrere Admins auf einem System gibt, mag es sinnvoll sein, deren Aktivitäten in einer root-Shell voneinander zu trennen. Dafür bieten sich einerseite getrennte screen
- oder tmux
-Sessions an, andererseits eine eigene shell history für jeden User. Es bietet sich an, an den üblichen Pfad der history-Datei (˜/.bash_history
) die UID oder den Accountnamen des Users, der sich root-Rechte verschafft hat, anzuhängen.
Der beste Weg dafür, jedenfalls auf Systemen, auf denen pam_loginuid
aktiv ist, ist /proc/self/loginuid
.
# use the login UID as suffix if [ "$EUID" -eq 0 ]; then if [ -f /proc/self/loginuid ]; then loginuid="$(< /proc/self/loginuid )" HISTFILE="${HOME}/.bash_history.${loginuid}" unset loginuid fi fi # use the login name as suffix if [ "$EUID" -eq 0 ]; then if [ -f /proc/self/loginuid ]; then loginuid="$(< /proc/self/loginuid )" HISTFILE="${HOME}/.bash_history.${loginuid}" if login_name="$( id -un "$loginuid" 2>/dev/null )" && [ -n "$login_name" ]; then HISTFILE="${HOME}/.bash_history.${login_name}" unset login_name fi unset loginuid fi fi
Typischerweise dürfte sinnvoller sein, jedes Kommando nur ein Mal in der history zu haben (also erasedups
zu verwenden).
Eine Ausnahme ist der Fall, in dem man sauber dokumentieren möchte, was auf einem System (in einem bestimmten Zeitraum) gemacht wurde. Es mag sinnvoll sein, dafür eine gesondere history-Datei zu setzen. Insbesondere mögen gesonderte history-Dateien sinnvoll sein, wenn man in mehreren Shells arbeitet, um die Probleme beim Zusammenführen zu vermeiden. Da dies nur die Eingaben erfasst, mag man zusätzlich script
(und dann scriptreplay
) verwenden.
Eine solche dedizierte Datei für die shell history könnte eine ähnliche Aktion in der Zukunft erleichtern, indem man eine Kopie dieser Datei in die neue Shell-Sitzung lädt (history -r
).
create_event_shell_history () { local hist_file_path read -p 'Enter the name of the shell event (no "/"): ' shell_event_name if [ -z "${shell_event_name}" ] || [[ $shell_event_name =~ / ]]; then printf '%s\n' 'ERROR: invalid input; aborting' return 1 fi tty="$( tty )" tty="${tty#/}" tty="${tty//\//-}" hist_file_path="${HOME}/.bash_history.${shell_event_name}.${tty}" if [ -f "${hist_file_path}" ]; then printf '%s\n' "ERROR: The history file '${hist_file_path}' already exists; aborting" return 1 fi printf '%s\n' "INFO: Using the file '${hist_file_path}' for the shell history; you can write it with 'history -w' (again and again)" HISTFILE="${hist_file_path}" HISTSIZE=10000 HISTFILESIZE=10000 HISTCONTROL= HISTTIMEFORMAT='%Y-%m-%d %H:%M:%S ' # clear history history -c # create history file history -w }
Eine weitere sehr häufig von mir genutzte Funktion ist das Einfügen des letzten Arguments der vorherigen Kommandozeile. Typische Fälle:
erstes Kommando
Ansicht einer Datei (oder des Elternverzeichnisses) mit ls
.
Kopieren oder Umbenennen einer Datei
Test eines Globbing-Ausdrucks (triviales Beispiel *.txt
) mit echo
zweites Kommando
Wechsel in das Verzeichnis
Editieren der Datei
Es gibt (mindestens) zwei "unkritische" Möglichkeiten, das letzte Argument der vorherigen Kommandozeile in die aktuelle einzufügen (auch mehrfach):
Eingabe von Alt-. (readline key binding yank-last-arg
)
die spezielle Variable $_
Die Alt-.-Variante ist in mehrfacher Hinsicht überlegen:
schneller zu tippen (zwei Tasten statt vier, auf deutscher Tastaturbelegung)
mehrfache Eingabe des Punktes bei gedrückter Alt-Taste (Alt-.-.) wechselt zum letzten Argument der jeweils vorherigen Kommandozeile
Die allgemeine Variante (beliebiges Wort der vorherigen Kommandozeile) ist die Kombination der readline-Funktion digit-argument
mit yank-nth-arg
. Die zuerst angegebene Ziffer ist der Index des Wortes der vorherigen Kommandozeile, angefangen mit null. Null ist das Kommandowort, Eins das erste Argument. digit-argument
wird standardmäßig aufgerufen mit Alt-n, wobei n für die jeweilige Ziffer steht.
Neben den oben genannten Readline key bindings gibt es das mächtigere bash-Feature history expansion. Ich konnte mich damit nie so recht anfreunden. Ich nutze es zu selten und habe deshalb zumeist die Syntax-Details vergessen, wenn ich es mal gebrauchen könnte. Zumeist ist (oder zumindest erscheint) es schlicht sehr viel schneller,
die key bindings für das letzte Argument einer der vorherigen Kommandozeilen zu nutzen
die fragliche Kommandozeile (über die Pfeiltasten oder die Suchfunktion) aufzurufen und zu editieren
die "Copy&Paste"-Funktion von Readline zu nutzen (siehe den folgenden Abschnitt)
oder die Maus zu benutzen, die man ja Typischerweise zur Verfügung hat...
Die grundsätzliche Vorgehensweise ist, dass der erste Teil des history expansion-Ausdrucks die Kommandozeile auswählt und der zweite Teil ein oder mehrere Worte daraus. Wenn keine Worte ausgewählt werden, wird die gesamte Zeile verwendet.
Eingeleitet wird der Ausdruck mit dem history expansion character, standardmäßig ein Ausrufezeichen. Doppelte Ausrufezeichen referenzieren die vorherige Kommandozeile (!!
entspricht !-1
).
Wenn man history expansion nutzen möchte (standardmäßig aktiv, kann mit set +H
/ set -H
(de-)aktiviert werden), erscheinen mir zwei Shell-Einstellungen sehr nützlich:
shopt -s histverify
sorgt dafür, dass eine Kommandozeile, die history expansion verwendet, nicht sofort ausgeführt, sondern erst in der endgültigen Version angezeigt wird und mit Return bestätigt werden muss.
\!
im Shell-Prompt zeigt die history-Nummer der nächsten Kommandozeile an, so dass es einfacher ist, die gewünschte Zeile (absolut) zu referenzieren. Eine andere Möglichkeit, diesen Wert zu erfahren, ist die Shell-Variable HISTCMD
.
Die Suche in der Shell history gehört für mich zu den wichtigsten Shell-Features überhaupt. Ich nutze es ständig, und es spart immens viel Zeit (und Nerven).
Es gibt mindestens vier Möglichkeiten, eine frühere Kommandozeile zu suchen:
history | grep wasauchimmer
Für die Shell ist das ein ganz normales Kommando, das keine Auswirkung auf den Shellprompt hat. Man muss das Ergebnis dann mit der Maus kopieren oder mit eval
herumtricksen.
Die inkrementelle Suche an beliebigen Stellen innerhalb der Kommandozeilen: reverse-search-history
Dies ist in vielen Distributionen standardmäßig auf ^R
(Ctrl-r). Nach der Eingabe von Ctrl-r tippt man die gesuchte Zeichenkette. Die Suche springt zum neusten passenden Eintrag in der history. Wenn durch weitere eingegebene Zeichen der vorherige Treffer nicht mehr passt, springt die Suche zum einem älteren Eintrag, der auf die neue Such-Zeichenkette passt, wenn es so einen Eintrag gibt. Eine erneute Eingabe von Ctrl-r springt zum nächstälteren passenden Eintrag in der history. Die Eingabe von Return führt die angezeigte Kommandozeile aus, mit den Pfeiltasten (rechts, links) kann man die angezeigte Kommandozeile in den Bearbeitungsmodus laden.
Die Suche in die Gegenrichtung ist die Readline-Funktion forward-search-history
, bei mir auf ^F
(standardmäßig ^S
, aber diese Eingabe wird typischerweise vom Terminalemulator oder Terminalmultiplexer (GNU screen, tmux) abgefangen). Durch wiederholte (oder wechselweise) Eingabe von ^R
und ^F
kann man in beide Richtungen durch die passenden Kommandozeilen wechseln.
Viel wichtiger als reverse-search-history
ist in meinem Alltag die Suchfunktion history-search-backward
; diese Funktion ist nicht allgemein voreingestellt. In SuSE Linux, woher ich die Funktion kenne, war sie auf PgUp (Bild hoch) gelegt; ich habe das immer so beibehalten. Das Gegenstück, history-search-forward
, auf PgDown.
Diese Funktion erwartet, dass der Cursor nicht am Zeilenanfang steht (unabhängig davon, ob man etwas getippt hat oder in ein frühere Kommandozeile gesprungen ist und dort den Cursor bewegt hat) und sucht nach Zeilen, die mit demselben Text beginnen (alles vom Zeilenanfang bis zum Cursor).
Dies ist sehr nützlich und schnell, weil in der Regel ein einziger Buchstabe ausreicht, um die Suche ausreichend einzugrenzen. Wenn man nach wenigen Eingaben von PgUp eine über die Cursorposition hinaus passende Zeile erreicht hat, kann man schnell den Cursor nach rechts bewegen, zeichen- oder wortweise, um die Suche zu präzisieren, und dann erneut PgUp eingeben.
Aus meiner Sicht nicht sonderlich praktisch, aber eben auch möglich ist die Suche mittels history expansion.
!zeilenanfang
und !?irgendwo in der zeile?
referenzieren die vorherige passende Kommandozeile.
Eine Möglichkeit, schnell und in leicht merkbarer Weise viele Einträge in der history zu adressieren, ergibt sich aus dem "Missbrauch" von Kommandozeilen-Umgebungsvariablen. Man kann einfach Variablen kreieren, die nicht benötigt werden, aber deren Namen sich leicht merken und schnell tippen lassen:
x_cd_a= cd /langer/pfad x_foo= command with many parameters
Die Einträge lassen sich dann leicht über x_fooPgUp
oder !?x_fooEnter
aufrufen. Man sollte dann aber ggf. darauf achten, das Kommando nicht zu editieren (ohne die Adressierungs-Variable zu löschen oder umzubenennen).
Neben der besseren Adressierbarkeit kann man dies z.B. auch dafür nutzen, unübersichtliche Kommandos leicht auseinanderhalten zu können.
Es ist allerdings grundsätzlich zu überlegen, ob es mehr Sinn ergibt, die Kommandozeile in eine Funktion oder einen Alias zu packen. Damit vermeidet man versehentliches Editieren. Das kann sehr störend werden, wenn man die Kommandozeile gelegentlich editieren muss. In dem Fall mag ein Alias die bessere Lösung sein, weil man den mit (z.B., s.u.) der Readline-Funktion shell-expand-line
auflösen und dann risikolos editieren kann: x_cd_a
Ctrl-Alt-e führt zu folgendem Text in der Kommandozeile (den man dann vor der Ausführung editieren kann): cd /langer/pfad
Zwei der von mir meistgenutzten Readline-Funktionen sind das wortweise Löschen von Teilen der Kommandozeile und das Einfügen des letzten oder eines früheren gelöschten Textes. Viele dieser Funktionen (diejenigen, die sich sinnvoll mehrfach unmittelbar hintereinander ausführen lassen) können mit einem numerischen Argument versehen werden (wie bei yank-nth-arg
), indem digit-argument
vorher aufgerufen wird (für Zahlen größer Neun mehrfach). Der Ringspeicher, der die gelöschten Texte aufnimmt, wird kill ring genannt.
Im kill ring werden nur diejenigen Löschaktionen gespeichert, die nicht mittels Backspace / Ctrl-h oder Del nur ein einzelnes Zeichen löschen, also etwa
Wort löschen (definiert als Gruppe alphanumerischer Zeichen, ohne quoting; außer bei unix-word-rubout
)
unix-word-rubout
(standardmäßig Ctrl-w) löscht das Wort (begrenzt durch Leerraum, ohne Berücksichtigung von quoting) links vom Cursor
Für diese Funktion gibt es keine Entsprechung in die andere Richtung.
backward-kill-word
(standardmäßig Alt-Backspace) löscht das Wort links vom Cursor
kill-word
(standardmäßig Alt-d) löscht das Wort rechts vom Cursor
Wort löschen (begrenzt durch einen nicht quoting-geschützten metacharacter)
shell-backward-kill-word
(bei mir Ctrl-Del) löscht das Wort links vom Cursor
shell-kill-word
(bei mir Alt-Del) löscht das Wort rechts vom Cursor
vom Cursor bis zum Zeilenanfang / Zeilenende löschen
unix-line-discard
(standardmäßig Ctrl-u) löscht alles vom Zeilenanfang bis zum Cursor
kill-line
(standardmäßig Ctrl-k) löscht alles vom Cursor bis zum Zeilenende
Pfadkomponenten löschen
Die Funktion unix-filename-rubout
(bei mir Shift-Ctrl-Del) löscht Pfadkomponenten (bzw. Text, der so aussieht) links vom Cursor. Technisch wird bis zum nächsten Slash (/
) oder Leerraum (ohne Beachtung von quoting) gelöscht.
Wenn unmittelbar hintereinander Readline-Funktionen (dieselbe oder unterschiedliche) ausgeführt werden, die Text in den kill ring kopieren, dann werden die betroffenen Texte zu einem einzigen Element im kill ring zusammengefügt.
Dies sind alles Beispiele für "cut & paste", nicht "copy & paste". Es gibt analoge Readline-Funktionen, die in den kill ring kopieren, ohne zu löschen, aber die benutze ich nie. Zu löschen und mit einer simplen Tastenkombination sofort wiedereinzufügen, erfüllt denselben Zweck. Sich doppelt so viele Tastenkombinationen zu merken, um dies zu vermeiden, erscheint wenig attraktiv. Das scheint eine verbreitete Haltung zu sein, denn keine dieser Funktionen hat eine standardmäßige Tastenkombination...
Das Einfügen des aktiven kill-ring-Elements erfolgt mit Ctrl-y (yank
).
Ein älteres Element des kill ring kann man einfügen, indem man zuerst mittels yank
das aktuelle Element einfügt und dann mit yank-pop
(standardmäßig Alt-y) durch den kill ring rotiert; dadurch wird das zuletzt eingefügte Element durch das nächstältere (bzw., am Ende des kill ring, durch das neuste) ersetzt.
Mit der Funktion undo
(standardmäßig Ctrl-_) kann man die Änderungen an der Kommandozeile einzeln rückgängig machen. Dabei werden alle ununterbrochenen Texteingaben ohne (effektive) Änderung der Cursorposition als eine einzige Änderung behandelt.
Mit der Funktion revert-line
(standardmäßig Alt-r) wird die Kommandozeile in ihren Ursprungszustand zurückversetzt (wie mehrfaches Ausführen von undo
). Für eine neue Kommandozeile bedeutet das, dass sie gelöscht wird. Sinnvoll ist das also in erster Linie, wenn man eine Kommandozeile aus der history editiert.
Man möchte nicht mit vielen Einzelschritten durch die Kommandozeile laufen, um Änderungen vorzunehmen. Das gilt insbesondere für langsame und / oder unzuverlässige Netzwerkverbindungen.
Es gibt mehrere sehr nützliche Readline-Funktionen, um den Cursor schnell und gezielt an die richtige Stelle zu bekommen. Viele dieser Funktionen (diejenigen, die sich sinnvoll mehrfach unmittelbar hintereinander ausführen lassen) können mit einem numerischen Argument versehen werden (wie bei yank-nth-arg
), indem digit-argument
vorher aufgerufen wird (für Zahlen größer Neun mehrfach):
ein Zeichen
forward-char
(standardmäßig Ctrl-f) geht ein Zeichen nach rechts
backward-char
(standardmäßig Ctrl-b) geht ein Zeichen nach links
ein Wort (definiert als Gruppe alphanumerischer Zeichen; ohne quoting)
forward-word
(standardmäßig Alt-f) geht ein Wort nach rechts
backward-word
(standardmäßig Alt-b) geht ein Wort nach links
Da ich diese Funktion ständig brauche und die Standardbelegung nicht ergonomisch finde, habe ich diese Funktionen bei mir auf Ctrl-Pfeil rechts und Ctrl-Pfeil rechts gelegt. So kann ich sehr ähnliche Funktionen (wortweise Cursorbewegung an unterschiedlichen Wortgrenzen) mit den Pfeiltasten in Kombination mit Ctrl oder Alt aufrufen, intuitiv und schnell.
ein Wort (begrenzt durch einen nicht quoting-geschützten metacharacter)
shell-forward-word
(bei mir Alt-Pfeil rechts) geht ein Wort nach rechts
shell-backward-word
(bei mir Alt-Pfeil links) geht ein Wort nach links
Anfang und Ende der Kommandozeile
beginning-of-line
(standardmäßig Ctrl-a) springt an den Anfang der Kommandozeile
end-of-line
(standardmäßig Ctrl-e) springt ans Ende der Kommandozeile
Sprung zu einem bestimmten Zeichen
character-search
(standardmäßig Ctrl-]) springt zum nächsten Vorkommen (nach rechts) des danach eingegebenen Zeichen
Die vorherige Eingabe eines numerischen Arguments erlaubt es, statt zum nächsten z.B. zum übernächsten Vorkommen zu springen.
character-search-backward
(standardmäßig Ctrl-Alt-]) springt zum nächsten Vorkommen (nach links) des danach eingegebenen Zeichen
Die vorherige Eingabe eines numerischen Arguments erlaubt es, statt zum nächsten z.B. zum übernächsten Vorkommen zu springen.
bei mehrzeiligen Kommandozeilen: eine physische Bildschirmzeile nach oben / unten
previous-screen-line
(bei mir Ctrl-Pfeil oben) geht eine Zeile gerade nach oben
next-screen-line
(bei mir Ctrl-Pfeil unten) geht eine Zeile gerade nach unten
overwrite-mode
(bei mir Ins) schaltet um zwischen den Modi Einfügen und Überschreiben. Diese Einstellung wird nach der Ausführung der Kommandozeile automatisch auf Einfügen zurückgesetzt.
Die mit großem Abstand wichtigste Funktion im Zusammenhang mit Vervollständigung ist complete
. Diese Funktion aktiviert das Shell-Feature Programmable Completion, das natürlich viel mächtiger ist als die eher oberflächlichen Readline-Vervollständigungen.
Es gibt aber gelegentlich Situationen, in denen Programmable Completion nicht greift, aber man trotzdem einen Pfad, Benutzerlogin, Hostnamen (siehe die bash-Variable $HOSTFILE
) oder einen Variablennamen vervollständigt haben will. Es gibt zwei Varianten der Vervollständigung:
complete-*
ermittelt die Anzahl möglicher Vervollständigungen. Bei genau einer Möglichkeit wird die Vervollständigung in der Kommandozeile ausgeführt. Mehr Möglichkeiten werden angezeigt (wenn der Wert der Readline-Variable completion-query-items
null oder größer als die Anzahl der Möglichkeiten ist), so dass man durch die Eingabe zusätzlichen Textes die Auswahl einschränken kann (immer wieder, bis nur noch eine Möglichkeit übrig ist).
possible-*-completions
zeigt die möglichen Vervollständigungen immer nur an, fügt aber auch eine eindeutige Vervollständigung nicht in die Kommandozeile ein.
Die verfügbaren Vervollständigungs-Funktionen sind:
complete-filename
(standardmäßig Alt-/)
complete-username
(standardmäßig Alt-~)
complete-variable
(standardmäßig Alt-$)
complete-hostname
(standardmäßig Alt-@)
Readline kann Pfade von Homeverzeichnissen (tilde expansion), Aliase und history-expansion-Ausdrücke auflösen; ebenso Variablen und alle Arten von Ersetzungen: command substitution, arithmetic expansion, pathname expansion (globbing) und process substitution. Allerdings werden doppelte Anführungszeichen dabei gelöscht, so dass die Bedeutung der Kommandozeile sich dadurch ändern kann.
Der Unterschied zwischen pathname expansion (globbing) und den anderen Funktionen ist, dass die anderen sich kollektiv für die gesamte Kommandozeile behandeln lassen. pathname expansion kann nur pro Ausdruck ausgeführt werden.
Die verfügbaren Auflösungs-Funktionen sind:
tilde-expand
(standardmäßig Alt-&)
history-expand-line
(standardmäßig Alt-^)
alias-expand-line
(kein Standard)
history-and-alias-expand-line
(kein Standard)
shell-expand-line
(standardmäßig Ctrl-Alt-e) löst alle Ausdrücke auf (außer pathname expansion und brace expansion)
glob-complete-word
(standardmäßig Alt-g)
glob-expand-word
(standardmäßig Ctrl-x *)
glob-list-expansions
(standardmäßig Ctrl-x g)
Readline bietet die Möglichkeit, mit set-mark
(standardmäßig Ctrl-@ oder Alt-Space) innerhalb einer Kommandozeile eine Markierung zu setzen. Der Bereich von der Markierung bis zur Cursorposition wird als Region bezeichnet. Die Region inkludiert die linke Grenze, aber nicht die rechte (unabhängig davon, ob die Markierung die linke oder rechte Grenze ist). Die Funktion exchange-point-and-mark
(standardmäßig Ctrl-x Ctrl-x) vertauscht die Positionen von Cursor und Markierung und markiert die Region farblich; zweifaches Ausführen erhält die ursprünglichen Positionen und die farbliche Markierung. Die Region kann gelöscht (und dadurch in den kill ring kopiert) oder direkt in den kill ring kopiert werden. Löschen erfolgt über die Funktion kill-region
(bei mir Shift-Del). Kopieren ist am einfachsten (wie sonst auch) durch Löschen und sofortiges Wiedereinfügen (Ctrl-y).
Dies ist nur selten nötig. Vorteile gegenüber den normalen Löschfunktionen sind,
dass man unabhängig von Wortgrenzen löschen kann
dass man sieht, was man löschen würde (wobei man bedenken muss, dass das Zeichen ganz rechts nicht enthalten ist); dies dürfte primär dann vorteilhaft sein, wenn man große Bereiche löschen oder kopieren will.
Wenn eine Kommandozeile zu komplex und / oder kompliziert ist, ist es oftmals sinnvoller, sie in eine Scriptdatei zu packen, weil der Editor mehr Möglichkeiten bietet als Readline und die Formatierung den Code sehr viel lesbarer machen kann.
Es gibt eine Zwischenlösung auf halber Strecke zwischen Kommandozeile und Scriptdatei. edit-and-execute-command
(standardmäßig Ctrl-x $VISUAL
oder $EDITOR
festgelegten Editor (oder mit emacs, falls beide leer / ungültig sind). Nach dem Speichern der Datei und dem Beenden des Editors wird der Inhalt der Datei als Kommandozeile ausgeführt.
upcase-word
(standardmäßig Alt-u), downcase-word
(standardmäßig Alt-l) und capitalize-word
(standardmäßig Alt-c) ändern (ggf.) die Groß- / Kleinschreibung des Worts (oder Wortteils) rechts vom Cursor.
Die schon mehrfach erwähnte Funktion digit-argument
erlaubt auch die wiederholte Eingabe eines Zeichens. Praktisch, wenn man genau eine bestimmte, größere Anzahl von Zeichen (und sei es zum Überschreiben) braucht: Alt-6 Alt-4 x
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Die Readline-Funktionen werden durch Kommandozeilen der Art bind '"\C-@":set-mark'
an Tasten(kombinationen) gebunden. Mit der Form bind '"\C-t":"statischer Text"'
kann eine feste Zeichenfolge eingefügt werden.
Das große praktische Problem mit den vielen Tastenkombinationen ist natürlich: Wenn man sie nicht häufig verwendet, ist es schwierig, sie sich zu merken. Dieses Problem lässt sich leicht entschärfen, indem man sich eine kleine Ausgabe der als relevant empfundenen Funktionen mit der jeweiligen Tastenkombination generiert.
Das kann man mit einer Shellfunktion machen
rlf () { printf '%s\n' 'possible-completions (M-?) complete-filename (M-/) complete-username (M-˜) complete-variable (M-$) complete-hostname (M-@) tilde-expand (M-&) history-expand-line (M-ˆ) shell-expand-line (M-C-e)' }
aber auch mit einer Readline-Tastenkombination (hier beispielhaft Ctrl-Alt-h, aber F1 bietet sich an, wenn die Taste nicht vor der Shell abgefangen wird), die einfach nur einen Text ausgibt (als auskommentiertes Kommando); was besser ist, sei mal dahingestellt:
bind '"\e\C-h":": \"possible-completions (M-?) # complete-filename (M-/) # complete-username (M-˜) # complete-variable (M-$) # complete-hostname (M-@) # tilde-expand (M-&) # history-expand-line (M-ˆ) # shell-expand-line (M-C-e)\" "'
Da man diese Information zumeist während der Bearbeitung einer Kommandozeile benötigt, bietet es sich an, die Bearbeitung zu unterbrechen, indem man (mit Ctrl-a) an den Anfang springt, dort ein Kommentarzeichen (#
) einfügt und die Kommandozeile dann "ausführt". Dann kann man sich die Onlinehilfe ausgeben lassen und sich die noch unfertige Kommandozeile aus der history holen.
Als Ergänzung zu den statischen Macros bietet Readline die Möglichkeit, (leider nur) eine Eingabe aufzuzeichnen (inklusive Steuerzeichen). Vorteile gegenüber der Nutzung des kill ring:
Man kann (was ggf. nur selten vorkommt) mit Eingaben arbeiten, die dem kill ring nicht zugänglich sind (Ctrl, Alt)
Je nach üblicher Arbeitsweise wird das aktuelle Element des kill ring ständig überschrieben.
Die Macro-Funktionen:
start-kbd-macro
(standardmäßig Ctrl-()
end-kbd-macro
(standardmäßig Ctrl-))
call-last-kbd-macro
(standardmäßig Ctrl-e)
print-last-kbd-macro
(kein Standard)
Das Verhalten von Readline kann durch einige Readline-Variablen (das sind keine Shell-Variablen) gesteuert werden. Wenn man in der man page von bash nach Readline Variables sucht, landet man direkt beim richtigen Abschnitt. Ich hatte nur bei einer das Bedürfnis, die zu ändern:
bind 'set show-all-if-ambiguous on'
sorgt dafür, dass man sich in dem häufigen Fall, dass es bei tab completion mehrere Möglichkeiten gibt, eine Eingabe von Tab sparen kann: Nach der ersten Eingabe werden die Möglichkeiten sofort angezeigt.
Es gibt zwei Möglichkeiten, die Readline-Konfiguration vorzunehmen:
in der Datei ˜/.inputrc
(für alle Applikationen, die Readline nutzen, gleichzeitig)
mittels des bash builtin bind (in den bash-Konfigurationsdateien oder im laufenden Betrieb)
Ich nutze Readline (bewusst) nur in bash, deshalb nutze ich die zweite Variante. In einer meiner bash-Konfigurationsdateien findet sich dieser Codeblock (der nur in interaktiven Shells ausgeführt werden soll):
if [[ $- =~ i ]]; then # BEGIN: search # <PgUp> : non-incremental search for the same beginning of a command line bind '"\e[5~":history-search-backward' # <PgDown> : non-incremental search for the same beginning of a command line bind '"\e[6~":history-search-forward' # <Ctrl>-F : ^S is usually caught by the terminal so changed to ^F bind '"\C-f":forward-search-history' # <Ctrl>-R bind '"\C-r":reverse-search-history' # END: search # BEGIN: delete # <Shift>-<Del> bind '"\e[3;2~":kill-region' # <Ctrl>-<Del> : words limited by unquoted metacharacter bind '"\e[3;5~":shell-backward-kill-word' # <Alt>-<Del> : words limited by unquoted metacharacter bind '"\e[3;3~":shell-kill-word' # <Shift>-<Ctrl>-<Del> : delete path elements from right to left (until slash or blank) bind '"\e[3;6~":unix-filename-rubout' # END: delete # BEGIN: move # <Ctrl>-<arrow left> : words limited by non-alphanumeric character bind '"\e[1;5D":backward-word' # <Ctrl>-<arrow right> : words limited by non-alphanumeric character bind '"\e[1;5C":forward-word' # <Alt>-<arrow left> : words limited by unquoted metacharacter bind '"\e[1;3D":shell-backward-word' # <Alt>-<arrow right> : words limited by unquoted metacharacter bind '"\e[1;3C":shell-forward-word' # <Ctrl>-<arrow up> : with a multi-line command line: go straight up one line bind '"\e[1;5A":previous-screen-line' # <Ctrl>-<arrow down> : with a multi-line command line: go straight down one line bind '"\e[1;5B":next-screen-line' # END: move # BEGIN: region # <Ctrl>-^ <Space> bind '"\C-^ ":set-mark' # <Ctrl>-^ x bind '"\C-^x":exchange-point-and-mark' # END: region # BEGIN: dynamic macro # <Ctrl>-^ <Ctrl>-m s bind '"\C-^\C-ms":start-kbd-macro' # <Ctrl>-^ <Ctrl>-m e bind '"\C-^\C-me":end-kbd-macro' # <Ctrl>-^ <Ctrl>-m c bind '"\C-^\C-mc":call-last-kbd-macro' # <Ctrl>-^ Ctrl>-m p bind '"\C-^\C-mp":print-last-kbd-macro' # END: dynamic macro # other # insert key: switch between insert and overwrite modes bind '"\e[2~":overwrite-mode' # <Ctrl>-^ l bind '"\C-^l":glob-list-expansions' # <Ctrl>-^ e bind '"\C-^e":edit-and-execute-command' # <F1> "online help" bind '"\eOP":": \" # set-mark (C-^ SPACE) # exchange-point-and-mark (C-^ x) # kill-region (<Shift>-<Del>) # complete-filename (M-/) # complete-username (M-˜) # complete-variable (M-$) # complete-hostname (M-@) # tilde-expand (M-&) # history-expand-line (M-ˆ) # glob-list-expansions (C-^ l) # shell-expand-line (M-C-e) # edit-and-execute-command (C-^ e)\" "' # BEGIN: set readline variables bind 'set show-all-if-ambiguous on' # END: set readline variables fi