Dotychczas pokazaliśmy jak dodać podstawowe uzupełnianie do poleceń, używając gotowych mechanizmów dostępnych w pakiecie bash. W drugiej części pokażemy jak dodać zupełnie nowe, własne uzupełnianie poleceń.
W części pierwszej zobaczyliśmy dodawanie uzupełniania nazw hostów do dowolnie wybranych poleceń używając:
complete -F _known_hosts xvncviewer
Metoda używa polecenia complete
do powiadomienia basha, że funkcja _known_hosts
powinna zostać użyta do obsługi uzupełniania argumentów dla xvncviewer
.
Jeśli chcemy dodać własne uzupełnianie do polecenia, musimy napisać w jej miejsce swoją własną funkcję, i skojarzyć ją z poleceniem.
Jako podstawowy przykład, spróbujemy dodać proste uzupełnianie do programu foo
. To przykładowe polecenie przyjmuje trzy argumenty:
foo
, i kończy wykonanie.
foo
, i kończy wykonanie.
foo
w trybie szczegółowego wyjścia
Do obsługi tych argumentów utworzymy nowy plik /etc/bash_completion.d/foo
. Plik ten będzie automatycznie dołączony (lub załadowany), gdy ładowany jest kod uzupełniania basha.
Wewnątrz tego pliku zapisz poniższą treść:
{literal} _foo() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="--help --verbose --version" if [[ ${cur} == -* ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } complete -F _foo foo{/literal}
Bo go przetestować, możesz teraz dołączyć plik:
skx@lappy:~$ . /etc/bash_completion.d/foo skx@lappy:~$ foo --TAB --help --verbose --version
Eksperymentując, przekonasz się, że argumenty są poprawnie uzupełniane, zgodnie z oczekiwaniami. Wpisanie foo --hTAB
powoduje uzupełnienie argumentu --help
. Wciśnięcie TAB kilka razy powoduje wyświetlenie listy wszystkich podpowiedzi. (W tym wypadku nie ma nawet znaczenia, że program o nazwie foo
nie jest dostępny w Twoim systemie.)
Skoro dysponujemy działającym przykładem, powinniśmy spojrzeć, w jaki sposób działa!
Poprzedni przykład pokazywał prostą funkcję basha, która była wywoływana do obsługi uzupełniania dla polecenia.
Funkcja na początku definiuje kilka zmiennych, cur
– bieżące wpisywane słowo, prev
– poprzednie wpisane słowo, oraz opts
– nasza lista opcji do uzupełnienia.
Uzupełnianie opcji jest następnie obsługiwane poprzez użycie komendy compgen
w następującym wywołaniu:
{literal} COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ){/literal}
Sprawia to ustawienie wartości zmiennej $COMPREPLY
na wyjście polecenia:
{literal} compgen -W "${opts}" -- ${cur}{/literal}
Gdyby zastąpić te zmienne ich wartościami, przekonamy się, jak ono działa:
compgen -W "--help --verbose --version" -- "userinput"
Polecenie usiłuje zwrócić dopasowanie bieżącego słowa {literal}${cur}{/literal}
do listy --help --verbose --version
. Po wywołaniu go w powłoce, będziesz w stanie przekonać się, jak to działa:
skx@lappy:~$ compgen -W "--help --verbose --version" -- -- --help --verbose --version skx@lappy:~$ compgen -W "--help --verbose --version" -- --h --help
Na początku widać, co się stanie, gdy użytkownik wpisze jedynie --
- wszystkie trzy opcje pasują, więc są zwracane. Przy drugiej próbie, użytkownik wprowadza --h
. I to wystarcza do jednoznacznego dopasowania --help
, więc to jest zwracane.
W naszej funkcji, po prostu ustawiamy nasz wynik jako COMPREPLY
, i kończymy wywołanie. To pozwala bashowi zastąpić bieżące słowo wyjściem. COMPREPLY
jest specjalną zmienną, która ma konkretne znaczenie wewnątrz basha. W procedurach uzupełniania używana jest do oznaczania wyniku próby uzupełniania.
Z dokumentacji basha (ang.) możemy dowiedzieć się opisu COMPREPLY
:
COMPREPLY Zmienna tablicowa, z której Bash czyta możliwe uzupełnienia wygenerowane przez funkcję powłoki wywoływaną przez podsystem programowalnego uzupełniania Możemy również dowiedzieć się, w jaki sposób znaleźliśmy bieżące słowo, używając tablicy
COMP_WORDS
do odszukania zarówno bieżącego, jak i poprzedniego słowa:COMP_WORDS Zmienna tablicowa składająca się z pojedynczych słów w bieżącym wierszu poleceń. Zmienna ta jest dostępna tylko w funkcjach powłoki wywoływanych przez podsystem programowalnego uzupełniania. COMP_CWORD Indeks tablicy {literal}${COMP_WORDS}{/literal}
słowa zawierającego bieżącą pozycję kursora. Zmienna ta jest dostępna tylko w funkcjach powłoki wywoływanych przez podsystem programowalnego uzupełniania
Wiele poleceń jest bardziej skomplikowane do uzupełnienia, i posiada liczne opcje zależne od poprzednich.
Jako przykład posłuży dostarczane z Xen polecenie xm, posiadające podstawowe opcje:
/etc/xen
o nazwie ConfigName
.Name
.
Przeważnie polecenie wywołuje się jako xm operation args
, gdzie args
różni się w zależności od wybranego argumentu operation
.
Konfiguracja prostego uzupełniania pierwszego słowa – operacji – może zostać obsłużone w ten sam sposób, co w naszym poprzednim przykładzie, przy czym operacje nie zaczynają się od prefiksu --
. Uzupełnienie argumentów operacji natomiast wymaga specjalnej obsługi.
Jak pamiętacie, mamy dostęp do poprzedniego słowa wiersza poleceń, i używając go, możemy rozróżnić akcje dla poszczególnych operacji.
Przykładowy kod wygląda następująco:
{literal} _xm() { local cur prev opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" # # The basic options we'll complete. # opts="console create list" # # Complete the arguments to some of the basic commands. # case "${prev}" in console) local running=$(for x in `xm list --long | grep \(name | grep -v Domain-0 | awk '{ print $2 }' | tr -d \)`; do echo ${x} ; done ) COMPREPLY=( $(compgen -W "${running}" -- ${cur}) ) return 0 ;; create) local names=$(for x in `ls -1 /etc/xen/*.cfg`; do echo ${x/\/etc\/xen\//} ; done ) COMPREPLY=( $(compgen -W "${names}" -- ${cur}) ) return 0 ;; *) ;; esac COMPREPLY=($(compgen -W "${opts}" -- ${cur})) return 0 } complete -F _xm xm{/literal}
Skonfigurowaliśmy początkowe uzupełnianie operacji i dodaliśmy specjalną obsługę dwóch operacji: create
i console
. W obu przypadkach używamy compgen do uzupełnienia wejścia w zależności od tekstu podanego przez użytkownika, porównując je z dynamicznie generowaną listą.
Dla operacji console
uzupełniamy na podstawie wyjścia poniższego polecenia:
{literal}xm list --long | grep \(name | grep -v Domain-0 | awk '{ print $2 }' | tr -d \){/literal}
Zwraca ono listę uruchomionych systemów Xen.
Dla operacji tworzenia, uzupełniamy na podstawie wyjścia poniższego polecenia:
{literal}for x in `ls -1 /etc/xen/*.cfg`; do echo ${x/\/etc\/xen\//} ; done{/literal}
Przekształca ono listing katalogu /etc/xen
w wyjście składające się z nazw plików zakończonych ciągiem .cfg
. Na przykład:
{literal} skx@lappy:~$ for x in `ls -1 /etc/xen/*.cfg`; do echo ${x/\/etc\/xen\//}; done etch.cfg root.cfg sarge.cfg steve.cfg x.cfg skx@lappy:~${/literal}
Używając polecenia compgen
, pokazaliśmy, jak dopasować wejście użytkownika do list konkretnych łańcuchów znaków, zarówno używając ustalonej listy możliwości, jak i wyjścia innych poleceń.
Możliwe jest również użycie nazw katalogów, nazw procesów oraz innych rzeczy. Pełen opis można zobaczyć w podręczniku basha, poprzez wywołanie polecenia man bash
.
Końcowy przykład pokazuje, jak uzupełniać nazwy plików i hostów w odpowiedzi na dwie pierwsze opcje:
{literal} # # Completion for foo: # # foo file [filename] # foo hostname [hostname] # _foo() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="file hostname" case "${prev}" in file) COMPREPLY=( $(compgen -f ${cur}) ) return 0 ;; hostname) COMPREPLY=( $(compgen -A hostname ${cur}) ) return 0 ;; *) ;; esac COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) } complete -F _foo foo{/literal}
Używając tych przykładów, powinniście móc stworzyć własne funkcje obsługi uzupełniania. W 95% przypadków będziecie potrzebować uzupełniania spośród zbioru dostępnych opcji, w pozostałych przypadkach będziecie mieć do czynienia z dynamicznym generowaniem argumentów, jak pokazaliśmy na przykładzie xm
.
Prawdopodobnie najlepszym podejściem jest rozbicie opcji na zbiór potoków wiersza poleceń i przetestowanie ich poza środowiskiem uzupełniania (po prostu, w powłoce), a następnie po prostu wklejenie gotowych poleceń do funkcji uzupełniania.