Wer kennt es nicht: Man schreibt gerade so schön in der Konsole einen kilometerlangen Bash-Einzeiler und hat gerade keine Lust die Halbzeilenlange Option seines Lieblingsbefehls – für die es, trotz dem das halbe Alphabet bei den Kurzoptionen noch frei ist, keine Abkürzung gibt – auszuschreiben, als man sich nun schon zum hundertsten (oder tausendsten) Male darüber ärgert, dass die Auto-Vervollständigung der Shell mal wieder nicht funktioniert. Man wollte schon immer einmal eine Vereinfachung haben, die einem genau hier hilft und den lästigen Tippaufwand stark verringert. Mit etwas Bash-Skripting kann hier nachgeholfen werden.
Mit TrueCrypt stand ich nämlich genau vor diesem Problem: Zwar funktionierte die vervollständigung der Dateinamen, aber die Optionen wollte einem die Bash nie vorschlagen. Da TrueCrypt aber mindestens eine der Optionen immer haben möchte, war das bei dem (in meinem Fall ansonsten sehr kurz gehaltenen Befehl) immer ein Ärgernis. Also suchte ich ein wenig und wurde fündig.
Kern der automatischen Vervollständigung von Optionen, Dateien, Devices und anderen Dingen (bei Git z.B. Remote-, Branch- und Tag-Namen) sind eine Reihe von kleinen Snippets im Verzeichnis /etc/bash_completion.d/.
In diesem Verzeichnis liegt für jede Gruppe von Anwendungen ein Shell-Skript, welches man für seine Anwendung ergänzen oder anpassen muss. Da für TrueCrypt noch kein solches Script existierte, schaute ich mich erstmal ein wenig bei anderen Anwendungen um.
Der erste Schritt ist es der Shell mitzuteilen, welche Funktion sie aufrufen muss, um für einen Befehl die möglichen Argumente zu erhalten. Dies erreicht man mit dem Befehl:
complete -F funcname progname
Während progname mitteilt, für welches Programm diese Regel zur Vervollständigung gibt, teilt -F funcname mit, dass eine (lokal definierte) Funktion funcname für mehr Details aufgerufen werden soll.
Beim Aufruf unserer Funktion werden dieser zwei Variablen im Environment Block übergeben. Diese spiegeln die aktuelle Kommandozeile wieder und geben einen überblick über den Status der Eingabe. Die Rückgabe der möglichen Optionen wird im Environment in der Variable COMPREPLY erwartet.
Um uns das Leben zu vereinfachen, können wir, da auf bestimmte Dinge häufiger zugegriffen werden muss, uns den Zugriff etwas vereinfachen und diese als lokale Variablen kopieren. Um Seiteneffekte zu vermeiden (alle Bash Completion Scripts laufen via Sourcing) sollten alle lokal genutzten Variablen mittels local auch als solche gekennzeichnet sein. Mit einer passenden Default-Action sieht unsere Funktion dann im Gerüst wie folgt aus:
_tc_options ()
{
local cur
COMPREPLY=()
cur=`_get_cword`
# Default complete with a filename
_filedir
}
Mit diesem Stumpf sind wir aber noch nicht wirklich weiter als ohne diese Erweiterung. Als nächsten Schritt müssen wir für eigene Erweiterungen diese Funktion ergänzen und Argumente, die so aussehen wie der Beginn einer Option, mit einer eigenen Behandlung versehen. Diese lässt sich recht einfach mit einem Case-Statement klären oder eine passende IF-Abfrage:
if [[ "$cur" == -* ]]; then
COMPREPLY=( $( compgen -W "$(_tc_options_from_help 2>/dev/null | tr -d '.,[]()' )" -- "$cur" ) )
else
# Default complete with a filename
_filedir
fi
Und da der Befehl so kryptisch ist, gibt’s eine kurze Einführung in Advanced Bash Crypting:
-
COMPREPLY=( ... )
Wir bauen ein Array
-
$( compgen ... -- "$cur" 2>/dev/null | ... )
Wir bauen eine Liste der Optionen im richtigen Format
-
-W "$(_func2)"
Wir fragen woanders nach einer Liste möglicher Optionen
-
tr -d '.,[]()'
Wir schmeißen ein paar Sonderzeichen weg
Wobei _func2 (in unserem Fall _tc_options_from_help) eine zeilenweise Ausgabe aller verfügbaren Optionen liefert. Okay: Fast geschaft. Aber wie kommt man nun an alle Befehlszeilenargumente, die von TrueCrypt erlaubt werden? Nunja, wozu hat man --help? Könnte Helfen, hat aber noch die Unschönheit namens „GUI-Popup zum Wegklicken“. Aber dazu gibt’s einen Workaround:
truecrypt -t --help
Setzen wir dies noch in einer weiteren Funktion um, so ergibt sich für das Auflisten aller verfügbaren Optionen von TrueCrypt etwa folgende Routine:
_tc_options_from_help ()
{
local prog
if [ $# -ge 1 ]; then
prog="$1"
else
prog="truecrypt"
fi
local i IFS=" "$'\t'$'\n'
for i in $(LC_ALL=C $prog -t --help)
do
case $i in
-*)
echo "${i%=*}"
;;
esac
done
}
Setzen wir nun alles zusammen und reinitialisieren die Autovervollständigung mit einem kurzen
. /etc/bash-completion
sollte man nach Eingabe von truecrypt - und der Betätigung der Tabulator-Taste eine Liste der Optionen erhalten.
Möchte man für seine Anwendung noch weitere Optionen beachten, z.B. um bei ping -i automatisch die Interfaces vorgeschlagen zu bekommen, kann dies über weitere Variablen realisiert werden: $COMP_WORDS liefert ein Feld mit allen bisher geschriebenen Argumenten, während in $COMP_CWORD der Index für das aktuelle Argument zu finden ist. Unter $COMP_LINE findet man zusätzlich die gesamte Befehlszeile im Überblick. Mehr Details finden sich zudem in dieser kleinen Artikelserie oder der wunderbaren Bash-Dokumentation.