Hotkey für sudo

21.01.2025

Ich gebe es zu: Ich verfalle ab und an in die gefährliche Angewohnheit einfach sudo -i einzutippen und dann mehrere Änderungen nacheinander zu machen. Dabei wäre es viel sicherer, nur die wirklich wichtigen Befehle mit einem sudo zu beginnen.

Das scheitert oft daran, dass mir mitten in der Kommandozeile einfällt, dass ein sudo davor gehört oder dass ich das Kommando schon abgeschickt habe, um dann gesagt zu bekommen, dass die gewünschte Operation als normaler Benutzer nicht geht. Also zurück an den Zeilenanfang und ein sudo davor gepappt. Wenn mir das zwei, dreimal passiert, ist der Weg zum sudo -i nicht weit.

Ach wie schöne wäre doch ein Hotkey, mit dem man den sudo Präfix einfach an und aus schalten kann. Am besten, ohne dass die Cursorposition verloren geht, sodass man direkt weiterschreiben kann.

sudo an/sudo aus

Nach dieser langen Vorrede kommt jetzt erst einmal ein kurzer Code-Schnipsel für die Bash:

# Setup ALT+S as a sudo hotkey
function toggle_prefix {
        local PREFIX="$1"

        if ! [ -z "${READLINE_POINT}" ]; then
                if [ "${READLINE_LINE:0:${#PREFIX}}" == "${PREFIX}" ]; then
                        READLINE_LINE=${READLINE_LINE:${#PREFIX}}
                        READLINE_POINT=$((${READLINE_POINT} - ${#PREFIX}))
                else
                        READLINE_LINE="${PREFIX}${READLINE_LINE}"
                        READLINE_POINT=$((${READLINE_POINT} + ${#PREFIX}))
                fi
        fi
}

bind -x '"\es":toggle_prefix "sudo "'

Fügt man diese in die .bashrc1 des eigenen Users ein, kann man zukünftig in der Bash per Alt+s den sudo Präfix ein- und ausschalten.

Das Script lässt sich auch für andere Präfixe verwenden. Wer also lieber einen git Hotkey haben möchte, kann die Zeile einfach in bind -x '"\es":toggle_prefix "git "' ändern. Wichtig ist das Leerzeichen hinter dem Präfix, da er ansonsten direkt vor die aktuelle Zeile gepackt wird.

Details

Die Syntax des bind-Kommandos ist gewöhnungs- und erklärungsbedürftig. Der Parameter -x sorgt dafür, dass das angegebene Kommando immer dann ausgeführt wird, wenn ein bestimmter Hotkey gedrückt wird. Um die Sache nicht zu einfach zu gestalten, sind Hotkey und Kommando nicht durch ein Leerzeichen, sondern durch einen Doppelpunkt getrennt. Außerdem muss alles in einem einzigen Parameter verpackt sein. Die gesamte Zeile ist dann noch in Hochkommas zu setzen, wenn sie Leerzeichen enthält.

Der Aufruf von toggle_prefix ist damit schon mal klar: ':toggle_prefix "sudo "'. Die Anführungszeichen sind dazu da, dass Leerzeichen im Parameter von toggle_prefix zu erhalten. Nun muss noch der Hotkey über eine Key-Sequence definiert werden.

Diese Key-Sequence ist in der Bash-Man-Page im Kapitel “Readline Key Bindings” ausführlich erklärt. Nachfolgend eine kurze Zusammenfassung, damit man nicht das ganze Kapitel lesen muss. Ich habe mich hierbei auf die wichtigsten Punkte beschränkt:

Definition einer “Key-Sequence”

Zuerst einmal sollte man wissen, dass jede Key-Sequence in Anführungszeichen gesetzt werden muss2. Gleich danach ist wichtig, dass sich auch Tastenfolgen ohne Modifizierer als Key-Sequence festlegen lassen. Will man also sich oder jemand anderes ärgern, lässt sich mit bind -x '"h":echo "hurz"' einfach der Buchstabe h als Hotkey definieren. Anschließend wird für jedes eingegebene h, der Text hurz auf der Konsole ausgegbeen und die Eingabezeile bleibt leer.

Mit dieser Methode lassen sich auch längere Tastensequenzen binden. Es muss also nicht unbedingt ein einzelner Hotkey sein.

Bleibt noch die Frage: Wie gibt man Modifzierer wie Ctrl oder Alt an? Dies geschieht mit einer durch einen Backslash eingeleiteten Escape-Sequenz, welche dem Editor Emacs entliehen ist:

SequenzBedeutung
\C-Control-Präfix
\M-Meta-Präfix
\eEscape-Zeichen
\\Unveränderter Backslash
\"Unverändertes Anführungszeichen (")
\'Unverändertes Hochkomma (')

Auch dem wenig aufmerksamen Leser wird aufgefallen sein, dass in dieser Tabelle die Alt Taste nicht vorkommt. Die über Alt erreichbaren Tasten, werden im Terminal als Escape-Sequenz ausgegeben. Das heißt, es wird zuerst ein Escape-Zeichen und anschließend eine Zeichenfolge gesendet, welche die Taste repräsentiert. So funktioniert das auch mit Funktionstasten.

Es gibt eine einfache Möglichkeit herauszufinden, welche Sequenz zu einer bestimmten Funktionstaste oder einer bestimmten Tastenkombination mit Alt gehört: Zuerst tippt man Strg+v. In den meisten Terminals aktiviert das einen Modus, welcher die nächste Tastensequenz einfach ausgibt, ohne diese zu interpretieren. Betätigt man also Strg+v und danach Alt+s erscheint auf der Konsole ^[s als Ausgabe. Die Zeichenfolge ^[ steht für das Escape-Zeichen und muss für unsere Zwecke mit \e übersetzt werden. Um also Alt+s zu binden, ist die Key-Sequence \es.

Als Übung versuchen wir es nochmal mit F7. Zuerst geben wir Strg+vF7 ein. Die Ausgabe ist: ^[[18~. Also müsste die korrekte Key-Sequence \e[18~ lauten. Und tatsächlich, nachdem man in Bash bind -x '"\e[18~":echo "test"' gibt ein Druck auf die F7 Taste tatsächlich test auf der Konsole aus.

Bei Tastenkombinationen mit der Ctrl-Taste sind solche Umstände nicht nötig. Hier gibt es ja schon den \C- Präfix. Allerdings muss man hier sehr aufpassen, dass die Tastenkombination nicht bereits belegt ist.

Und bei der Meta-Taste ist es abhängig vom Terminal und der verwendeten Benutzeroberfläche, ob sich solche Tastenkombinationen überhaupt eingeben lassen und welche Taste dafür verwendet wird. Wer möchte, kann gerne danach Googeln und sich seine Zeit mit dem Lesen von länglichen Stackexchange-Artikeln vertreiben.

Und sonst noch…

Bleibt noch die Funktion toggle_prefix. Sie verwendet die beiden Variablen READLINE_POINT und READLINE_LINE, um das in PREFIX definierte Präfix ein- und auszuschalten. Damit diese Variablen gesetzt sind, muss bind mit dem Schalter -x aufgerufen werden. Dann enthalten READLINE_LINE den aktuellen Eingabepuffer und READLINE_POINT die Position der Eingabemarke. Änderungen an diesen beiden Variablen werden automatisch nach dem Ende der Funktion durch die Shell übernommen.

Die Funktion toggle_prefix prüft zuerst, ob die in READLINE_LINE gespeicherte Zeile bereits mit dem Präfix beginnt. Ist dies der Fall, werden so viele Zeichen vom Anfang von READLINE_LINE entfernt, wie das Präfix lang ist. Fehlt das Präfix, wird es einfach am Anfang hinzugefügt.

Damit sich die Position der Einfügemarke innerhalb der Zeile nicht verändert, muss deren Position um die Länge des Präfixes korrigiert werden. Dies geschieht, indem READLINE_POINT entsprechend erhöht oder verringert wird.


  1. Oder .bash_profile oder was auch immer… ↩︎

  2. Vergisst man die Anführungszeichen, erhält man folgende, komische Fehlermeldung: Das erste Zeichen ist nicht '\'. ↩︎