{"id":1393,"date":"2013-03-12T19:11:00","date_gmt":"2013-03-12T18:11:00","guid":{"rendered":"http:\/\/blog.benny-baumann.de\/?p=1393"},"modified":"2013-03-12T19:58:50","modified_gmt":"2013-03-12T18:58:50","slug":"automatische-vollstandigkeit","status":"publish","type":"post","link":"https:\/\/blog.benny-baumann.de\/?p=1393","title":{"rendered":"Automatische Vollst\u00e4ndigkeit"},"content":{"rendered":"<p>Wer kennt es nicht: Man schreibt gerade so sch\u00f6n in der Konsole einen kilometerlangen Bash-Einzeiler und hat gerade keine Lust die Halbzeilenlange Option seines Lieblingsbefehls &#8211; f\u00fcr die es, trotz dem das halbe Alphabet bei den Kurzoptionen noch frei ist, keine Abk\u00fcrzung gibt &#8211; auszuschreiben, als man sich nun schon zum hundertsten (oder tausendsten) Male dar\u00fcber \u00e4rgert, dass die Auto-Vervollst\u00e4ndigung der Shell mal wieder nicht funktioniert. Man wollte schon immer einmal eine Vereinfachung haben, die einem genau hier hilft und den l\u00e4stigen Tippaufwand stark verringert. Mit etwas Bash-Skripting kann hier nachgeholfen werden.<!--more--><\/p>\n<p>Mit TrueCrypt stand ich n\u00e4mlich genau vor diesem Problem: Zwar funktionierte die vervollst\u00e4ndigung der Dateinamen, aber die Optionen wollte einem die Bash nie vorschlagen. Da TrueCrypt aber mindestens eine der Optionen immer haben m\u00f6chte, war das bei dem (in meinem Fall ansonsten sehr kurz gehaltenen Befehl) immer ein \u00c4rgernis. Also suchte ich ein wenig und wurde f\u00fcndig.<\/p>\n<p>Kern der automatischen Vervollst\u00e4ndigung von Optionen, Dateien, Devices und anderen Dingen (bei Git z.B. Remote-, Branch- und Tag-Namen) sind eine Reihe von kleinen Snippets im Verzeichnis <tt>\/etc\/bash_completion.d\/<\/tt>.<\/p>\n<p>In diesem Verzeichnis liegt f\u00fcr jede Gruppe von Anwendungen ein Shell-Skript, welches man f\u00fcr seine Anwendung erg\u00e4nzen oder anpassen muss. Da f\u00fcr TrueCrypt noch kein solches Script existierte, schaute ich mich erstmal ein wenig bei anderen Anwendungen um.<\/p>\n<p>Der erste Schritt ist es der Shell mitzuteilen, welche Funktion sie aufrufen muss, um f\u00fcr einen Befehl die m\u00f6glichen Argumente zu erhalten. Dies erreicht man mit dem Befehl:<\/p>\n<pre lang=\"bash\">complete -F funcname progname<\/pre>\n<p>W\u00e4hrend <tt>progname<\/tt> mitteilt, f\u00fcr welches Programm diese Regel zur Vervollst\u00e4ndigung gibt, teilt <tt>-F funcname<\/tt> mit, dass eine (lokal definierte) Funktion <tt>funcname<\/tt> f\u00fcr mehr Details aufgerufen werden soll.<\/p>\n<p>Beim Aufruf unserer Funktion werden dieser zwei Variablen im Environment Block \u00fcbergeben. Diese spiegeln die aktuelle Kommandozeile wieder und geben einen \u00fcberblick \u00fcber den Status der Eingabe. Die R\u00fcckgabe der m\u00f6glichen Optionen wird im Environment in der Variable <tt>COMPREPLY<\/tt> erwartet.<\/p>\n<p>Um uns das Leben zu vereinfachen, k\u00f6nnen wir, da auf bestimmte Dinge h\u00e4ufiger 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 <tt>local<\/tt> auch als solche gekennzeichnet sein. Mit einer passenden Default-Action sieht unsere Funktion dann im Ger\u00fcst wie folgt aus:<\/p>\n<pre lang=\"bash\">\r\n_tc_options ()\r\n{\r\n    local cur\r\n\r\n    COMPREPLY=()\r\n    cur=`_get_cword`\r\n\r\n    # Default complete with a filename\r\n    _filedir\r\n}\r\n<\/pre>\n<p>Mit diesem Stumpf sind wir aber noch nicht wirklich weiter als ohne diese Erweiterung. Als n\u00e4chsten Schritt m\u00fcssen wir f\u00fcr eigene Erweiterungen diese Funktion erg\u00e4nzen und Argumente, die so aussehen wie der Beginn einer Option, mit einer eigenen Behandlung versehen. Diese l\u00e4sst sich recht einfach mit einem Case-Statement kl\u00e4ren oder  eine passende IF-Abfrage:<\/p>\n<pre lang=\"bash\" escaped=\"true\">\r\n    if [[ \"$cur\" == -* ]]; then\r\n        COMPREPLY=( $( compgen -W \"$(_tc_options_from_help 2&gt;\/dev\/null | tr -d '.,[]()' )\" -- \"$cur\" ) )\r\n    else\r\n        # Default complete with a filename\r\n        _filedir\r\n    fi\r\n<\/pre>\n<p>Und da der Befehl so kryptisch ist, gibt&#8217;s eine kurze Einf\u00fchrung in Advanced Bash Crypting:<\/p>\n<ul>\n<li>\n<pre lang=\"bash\">COMPREPLY=( ... )<\/pre>\n<p> Wir bauen ein Array<\/li>\n<li>\n<pre lang=\"bash\" escaped=\"true\">$( compgen ... -- \"$cur\" 2&gt;\/dev\/null | ... ) <\/pre>\n<p> Wir bauen eine Liste der Optionen im richtigen Format<\/li>\n<li>\n<pre lang=\"bash\">-W \"$(_func2)\"<\/pre>\n<p> Wir fragen woanders nach einer Liste m\u00f6glicher Optionen<\/li>\n<li>\n<pre lang=\"bash\">tr -d '.,[]()'<\/pre>\n<p> Wir schmei\u00dfen ein paar Sonderzeichen weg<\/li>\n<\/ul>\n<p>Wobei <tt>_func2<\/tt> (in unserem Fall <tt>_tc_options_from_help<\/tt>) eine zeilenweise Ausgabe aller verf\u00fcgbaren Optionen liefert. Okay: Fast geschaft. Aber wie kommt man nun an alle Befehlszeilenargumente, die von TrueCrypt erlaubt werden? Nunja, wozu hat man <tt>--help<\/tt>? K\u00f6nnte Helfen, hat aber noch die Unsch\u00f6nheit namens &#8222;GUI-Popup zum Wegklicken&#8220;. Aber dazu gibt&#8217;s einen Workaround:<\/p>\n<pre lang=\"bash\">truecrypt -t --help<\/pre>\n<p>Setzen wir dies noch in einer weiteren Funktion um, so ergibt sich f\u00fcr das Auflisten aller verf\u00fcgbaren Optionen von TrueCrypt etwa folgende Routine:<\/p>\n<pre lang=\"bash\">\r\n_tc_options_from_help ()\r\n{\r\n    local prog\r\n\r\n    if [ $# -ge 1 ]; then\r\n        prog=\"$1\"\r\n    else\r\n        prog=\"truecrypt\"\r\n    fi\r\n\r\n    local i IFS=\" \"$'\\t'$'\\n'\r\n    for i in $(LC_ALL=C $prog -t --help)\r\n    do\r\n        case $i in\r\n            -*)\r\n                echo \"${i%=*}\"\r\n                ;;\r\n        esac\r\n    done\r\n}\r\n<\/pre>\n<p>Setzen wir nun alles zusammen und reinitialisieren die Autovervollst\u00e4ndigung mit einem kurzen<\/p>\n<pre lang=\"bash\">. \/etc\/bash-completion<\/pre>\n<p>sollte man nach Eingabe von <tt>truecrypt -<\/tt> und der Bet\u00e4tigung der Tabulator-Taste eine Liste der Optionen erhalten.<\/p>\n<p>M\u00f6chte man f\u00fcr seine Anwendung noch weitere Optionen beachten, z.B. um bei ping -i automatisch die Interfaces vorgeschlagen zu bekommen, kann dies \u00fcber weitere Variablen realisiert werden: <tt>$COMP_WORDS<\/tt> liefert ein Feld mit allen bisher geschriebenen Argumenten, w\u00e4hrend in <tt>$COMP_CWORD<\/tt> der Index f\u00fcr das aktuelle Argument zu finden ist. Unter <tt>$COMP_LINE<\/tt> findet man zus\u00e4tzlich die gesamte Befehlszeile im \u00dcberblick. Mehr Details finden sich zudem in <a href=\"http:\/\/www.debian-administration.org\/articles\/316\">dieser kleinen Artikelserie<\/a> oder der <a href=\"https:\/\/www.gnu.org\/software\/bash\/manual\/bash.html#Programmable-Completion\">wunderbaren Bash-Dokumentation<\/a>.<\/p>\n<p class=\"wp-flattr-button\"><a href=\"https:\/\/blog.benny-baumann.de\/?flattrss_redirect&amp;id=1393&amp;md5=8bab619402c99795880174cf79156366\" title=\"Flattr\" target=\"_blank\"><img src=\"http:\/\/blog.benny-baumann.de\/wp-content\/plugins\/flattr\/img\/flattr-badge-large.png\" srcset=\"http:\/\/blog.benny-baumann.de\/wp-content\/plugins\/flattr\/img\/flattr-badge-large.png\" alt=\"Flattr this!\"\/><\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>Wer kennt es nicht: Man schreibt gerade so sch\u00f6n in der Konsole einen kilometerlangen Bash-Einzeiler und hat gerade keine Lust die Halbzeilenlange Option seines Lieblingsbefehls &#8211; f\u00fcr die es, trotz dem das halbe Alphabet bei den Kurzoptionen noch frei ist, keine Abk\u00fcrzung gibt &#8211; auszuschreiben, als man sich nun schon zum hundertsten (oder tausendsten) Male [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[29],"tags":[324,10,98,13,217],"class_list":["post-1393","post","type-post","status-publish","format-standard","hentry","category-software","tag-bash","tag-debian","tag-developement","tag-patch","tag-truecrypt"],"_links":{"self":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1393"}],"version-history":[{"count":5,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1393\/revisions"}],"predecessor-version":[{"id":1398,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1393\/revisions\/1398"}],"wp:attachment":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}