10 stycznia 2008

Mechanizm uzupełniania w powłoce bash

W natłoku wrażeń dzisiaj kolejne, tym razem niezwiązane bezpośrednio z Javą - mechanizm uzupełniania (completion) w powłoce bash.

Dużo czasu spędzam w powłoce bash pod Cygwin (i z wielką przyjemnością przesiadłbym się na Linux bądź OS/X, aby już więcej nie opuszczać linii poleceń), więc kiedy dzisiaj podczas lektury dokumentacji do buildr, a później dokumentacji jego protoplasty rake natrafiłem na artykuł Martina Fowlera Using the Rake Build Language, gdzie w sekcji Further Reading wskazano na mechanizm uzupełniania w bashu o mało nie spadłem z krzesła. Takiego wrażenia nie wywołało u mnie nawet pojawienie się finalnej wersji Java EE 5 ;-) I najbardziej bolesna była świadomość, że funkcjonalność uzupełniania basha była dostępna na wyciągnięcie ręki (!)

W ramach udoskonalania warsztatu wszystko inne zeszło na plan drugi i zabrałem się za poznawanie tego cudeńka. Najpierw zaczęło się od wspomnianego artykułu, a w zasadzie wpisu w blogu Rake tab completion for bash, a później cała masa artykułów na ten temat, gdzie najbardziej interesujące wydały mi się An introduction to bash completion: part 1 oraz An introduction to bash completion: part 2 i na zakończenie CLI Magic: Bash complete.

O co chodzi w tym bash completion, czy jakbym to nazwał uzupełnianiem w bashu. Każdy kto korzystał z basha zapewne korzystał z klawisza TAB, który uzupełniał listę plików przy poleceniu, czy samo polecenie.

jlaskowski@dev ~
$ [TAB] [TAB]
Display all 4545 possibilities? (y or n)

jlaskowski@dev ~
$ java [TAB] [TAB]
java.exe java2groovy javac.exe javadoc.exe javap.exe javaws.exe
java.exe.stackdump java2groovy.bat javacpl.cpl javah.exe javaw.exe
Jeśli jednak wpisałbym polecenie to dwukrotne wciśnięcie klawisza TAB spowoduje wyświetlenie listy plików z bieżącego katalogu lub uzupełnienie nazwy pliku, jeśli rozpoczęliśmy wpisywanie jego nazwy. Na bazie poprzedniego projektu dla buildr:

jlaskowski@dev /cygdrive/c/projs/sandbox/buildr-projekt
$ buildr bui[TAB] [TAB]
i mechanizm uzupełniania w bashu uzupełni do

jlaskowski@dev /cygdrive/c/projs/sandbox/buildr-projekt
$ buildr buildfile
To zapewne każdy znał, więc czym się tu zachwycać (nie wspominając o tym, po co było w ogóle to przywoływać skoro wszyscy to znają). To jednak nie wszystko. Tu gdzie rozpoczyna się prawdziwa jazda to plik /etc/bash_completion oraz katalog konfiguracyjny /etc/bash_completion.d/, w którym skrypty rozszerzają możliwości uzupełniania (podobnie działa rake i tym samym buildr w kontekście rozszerzeń poleceń użytkownika - o tym jednak napiszę, kiedy będzie o buildr)

jlaskowski@dev ~
$ ls -l /etc/bash_completion
bash_completion bash_completion.d/
Wystarczy zatem wczytać zawartość pliku /etc/bash_completion do powłoki i świat staje się ciekawszy.

jlaskowski@dev ~
$ complete -p

jlaskowski@dev ~
$ . /etc/bash_completion

jlaskowski@dev ~
$ complete -p
complete -F _mmsitepass mmsitepass
complete -F _withlist withlist
complete -F _list_members list_members
complete -F _remove_members remove_members
complete -o filenames -F _filedir_xspec oodraw
complete -o filenames -F _filedir_xspec elinks
complete -o filenames -F _filedir_xspec freeamp
complete -o filenames -F _java java
complete -o filenames -F _longopt split
complete -o filenames -F _longopt sed
complete -o filenames -F _longopt grep
complete -o filenames -F _longopt ld
complete -F _kill kill
complete -F _renice renice
complete -j -P '%' jobs
....
complete -o filenames -F _root_command sudo
complete -o filenames -F _jar jar
complete -o default -o filenames -F _mount mount
complete -a unalias

jlaskowski@dev ~
$ complete -p | wc -l
361
Teraz pozostaje z tego brać garściami, tj. uzupełniać polecenia i...ich argumenty (!)

Weźmy dla przykładu polecenie su (pozwalające przełączyć aktualnego użytkownika). Bez uzupełniania mamy jedynie uzupełnienie polecenia o dostępne pliki w bieżącym katalogu, co w tym przypadku czyni klawisz TAB kompletnie nieprzydatnym.

jlaskowski@dev ~
$ su [TAB] [TAB]
.bash_history .bashrc
Jednakże po wczytaniu pliku /etc/bash_completion klawisz TAB może nas zaskoczyć - pojawia się możliwość uzupełnienia użytkowników (!)

jlaskowski@dev ~
$ . /etc/bash_completion

jlaskowski@dev ~
$ su [TAB] [TAB]
Administrator Administrators Guest SYSTEM jlaskowski
Zatem mamy możliwość uzupełnienia pewnych poleceń systemowych o ich możliwe i dozwolone argumenty.

Pytanie, które mógłby ktoś zadać, to czy istnieje możliwość utworzenia własnej listy argumentów, jakie podamy poleceniu po dwukrotnym wciśnięciu klawisza TAB. Odpowiedź jest twierdząca. W podanych artykułach można doczytać szczegóły, ale teraz wiem, że mogę stworzyć odpowiedni plik konfiguracyjny dla polecenia complete i uzupełniać polecenia skryptów, których składnię każdorazowo musiałem pamiętać i dodatkowo ręcznie wpisywać. Tym razem koniec - korzystam z complete.

Przykładowy plik konfiguracyjny dla polecenia openejb z projektu Apache OpenEJB.

jlaskowski@dev ~
$ cat /etc/bash_completion.d/openejb
# openejb completion
#
# $Rev$ $Date$

_openejb()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="deploy properties start stop undeploy validate"

if [[ ${cur} == * ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -F _openejb openejb
Po wczytaniu tego pliku, co dzieje się automatycznie podczas wczytywania głównego pliku konfiguracyjnego /etc/bash_completion, każdorazowe wywołanie polecenia openejb i podwójny TAB spowodują wyświetlenie dostępnych poleceń.

jlaskowski@dev ~
$ openejb [TAB][TAB]
deploy properties start stop undeploy validate
Tego mi było trzeba. Kiedy jestem na projekcie i przydziela mi się zadanie zestawienia środowiska na Uniksie, każdorazowo dodaję zestaw aliasów, jak psj (wyświetl listę procesów java), psjc (liczba procesów java), czy różnego rodzaju przejścia do odpowiednich katalogów, które teraz rozbuduję o zestaw dostępnych uzupełnień poleceń startujących serwery aplikacyjne. Już nie mogę doczekać się pierwszego wdrożenia cudeńka produkcyjnie. Unix nie przestaje mnie zadziwiać, a korzystam z niego dłużej niż z Javy. Aż strach pomyśleć jak to wpłynie na stan mojego serca, jeśli wziąść pod uwagę ile takich ciekawostek jest jeszcze przede mną i w Uniksie, i w samej Javie ;-)