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ść:
_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
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:
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
Sprawia to ustawienie wartości zmiennej $COMPREPLY na wyjście polecenia:
compgen -W "${opts}" -- ${cur}
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 ${cur} 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_WORDSdo 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 ${COMP_WORDS}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:
_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
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:
xm list --long | grep \(name | grep -v Domain-0 | awk '{ print $2 }' | tr -d \)
Zwraca ono listę uruchomionych systemów Xen.
Dla operacji tworzenia, uzupełniamy na podstawie wyjścia poniższego polecenia:
for x in `ls -1 /etc/xen/*.cfg`; do echo ${x/\/etc\/xen\//} ; done
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:
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:~$
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:
#
# 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
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.