Portál AbcLinuxu, 20. dubna 2024 03:06

Bash: chytré doplňování

13. 8. 2007 | Michal Vyskočil
Články - Bash: chytré doplňování  

Je obecně známým faktem, že unixové shelly zvládají doplnit název souboru či adresáře. Ty lepší z nich dokáží nejen to, ale i doplnit parametry příkazů, nebo dokonce jejich hodnoty. Díky bash-completion se k nim může řadit i bash, nejpoužívanější shell v Linuxu.

Úvodem

Mnoho nováčků v Linuxu je znechuceno psaním šílených příkazů jako

tar -xzf foo_bar-4.1.76-r2.i386.tar.gz

protože opsat název balíčku správně je otročina a začnou nadávat na to, jak je Linux složitý. Ovšem pokud přijdou na to, že stačí napsat tar -xzf a stisknout Tab, tak jim shell automaticky doplní zbytek názvu souboru a Linux se stane o něco příjemnějším pracovním nástrojem. Ovšem bash (nemluvě o tcsh nebo zsh) toho umí daleko více.

Doplňování v praxi

Upozornění: následující příklady očekávají readline s volbou set show-all-if-ambiguous on, to znamená, že se možnosti objeví už po prvním stisku klávesy Tab. Zkontrolujte svůj .inputrc nebo /etc/inputrc, pokud to chcete mít stejně.

Existuje adresář a v něm následující soubory:

$ ls -1
foo_bar-4.1.76-r2.i386.tar.gz
foo_bar-4.1.76-r2.i386.tar.bz2
foo_bar-4.1.76-r2.i386.tar.gz.md5
foo_bar-4.1.76-r2.i386.pdf

Po napsání tar -xzf bash doplní jen soubor s příponou tar.gz a nebude nabízet pdf, či tar.bz2 soubor. Po napsání tar -xjf zase nabídne pouze soubor .tar.bz2.

Jak je psáno v článku Příkazový řádek - přítel nejvěrnější od pana Pavla Satrapy - bash od verze 2 nabízí příkaz complete, který dokáže ovládat doplňování jmen. Není nutné jej nějak moc ovládat. Snad jen to, že příkaz complete bez parametrů vypíše aktuální nastavení (pro roota nebývá, alespoň v Debianu, nastaveno nic). Příklady jeho použítí najdete ve zmiňovaném článku.

Příkaz complete (bez použití funkcí z bash-completion) umí takzvané základní doplňování:

Ovšem na dnešních systémech se používá programovatelné doplňování, třeba pro příkaz tar. Pro dpkg (bere jen argumenty s koncovkou deb) vypadá nastavení takto:

$ complete | grep dpkg$
complete -o filenames -F _dpkg dpkg

Což je odlišné od complete -A file -X '!*.deb' dpkg, které by odpovídalo příkladům uvedeným v článku.

Dnešní bash už dokáže nejen doplňovat názvy souborů či adresářů nebo filtrovat výsledky podle koncovky, ale jeho možnosti jsou plně programovatelné, což je ostatně vidět na výše odkazovaném příkladu s programem tar. Kompletní doplňování pak může vypadat např. takto (pro přehlednost uvádím doplněný příkaz na novém řádku):

$ tar [TAB]
A  c  d  r  t  u  x
$ tar xjf [TAB]
$ tar xjf foo_bar-4.1.76-r2.i386.tar.bz2

Pro tar je podpora doplňování nedokonalá, ale uživatelé Debianu si ji mohou "užít" například v programu aptitude (pravděpodobně stejně to bude fungovat i pro apt-get). Uživatelé ostatních distribucí nechť v diskusi oznámí, zda a jak to umí i ta jejich.

$ apti[TAB]
$ aptitude se[TAB]
$ aptitude search foo[ENTER]
...
$ aptitude in[TAB]
$ aptitude install foom[TAB]
$ aptitude install foomatic-[TAB]
foomatic-bin      foomatic-db-engine      foomatic-db-gutenprint  foomatic-filters  foomatic-gui
foomatic-db       foomatic-db-gimp-print  foomatic-db-hpijs       foomatic-filters-ppds

Bash tedy dokáže nejen doplnit název příkazu nebo parametry, ale navíc dokáže doplňovat i ze seznamu dostupných balíčků, což je vlastnost, která se už bez nějakého toho programování neobejde. Z bezpečnostních důvodů ovšem nejsou podobné "legrácky" povoleny uživateli root, takže zmíněné doplňování si mohou užít pouze běžní uživatelé.

Možným řešením je vytvořit speciální účet s minimálními právy a tyto příkazy pak spouštět pomocí sudo. Protože doplňování běží pod aktuálním uživatelem, není třeba se bát o bezpečnost systému, root do procesu vstoupí až po potvrzení (doslova vložení) příkazu.

Jak to funguje

Klíčem ke všemu je soubor /etc/bash_completion (resp. soubory v adresáři /etc/bash_completion.d/ pro každou aplikaci zvlášť), což není nic jiného, než shellový skript, takže, pokud doplňování nefunguje, stačí napsat

source /etc/bash_completion

Pokud jej nemáte, tak se podívejte po balíčku s názvem bash-completion. Počet příkazů, kterým bash umí "napovídat", je velký. Pro konkrétní číslo se se stačí podívat.

cat /etc/bash_completion /etc/bash_completion.d/* | grep ^_ | wc -l

Ve výše zmíněném příkladu s dpkg (complete -o filenames -F _dpkg dpkg) je důležitý parametr -F, který volá obslužnou funkci _dkpg. Tu shell po stisknutí tabulátoru volá a ta má na svědomí ono doplňování parametrů. Pro výpis aktuálních funkcí stačí napsat set | less.

Jak napsat vlastní funkci

Kostra doplňovací funkce vypadá takto:

  1 _foo()
  2 {
  3     local cur prev opts
  4     COMPREPLY=()
  5     cur="${COMP_WORDS[COMP_CWORD]}"
  6     prev="${COMP_WORDS[COMP_CWORD-1]}"
  7     opts="--ham --spam"
  8
  9     if [[ ${prev} != -* ]] ; then
 10         COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
 11         return 0
 12     fi
 13 }

a nastavení shellu complete -F _foo foo. Jméno funkce bývá stejné jako příkaz, který doplňuje, ale není to pravidlem (viz třeba _known_hosts). Dobrým zvykem bývá tyto funkce uvozovat podtržítkem.

Na řádku tři se pouze vytvoří lokální proměnné cur, prev a opts. COMPEREPLY je potom výstupní pole, z něhož čte bash jednotlivé možnosti. COMP_WORDS je speciální proměnná obsahující už napsané znaky, takže na řádku 5 se do proměnné cur uloží poslední napsané slovo a do prev to předposlední. COMP_CWORD tedy není nic jiného, než index do pole COMP_WORDS. Bash tyto proměnné naplňuje pouze pro funkce, které jsou volány příkazem complete.

Proměnná opts (řádek 7) potom obsahuje parametry hypotetického příkazu foo. A na řádku 9 je podmínka, která zařídí spuštění funkce pro doplnění jen v případě, že poslední napsané slovo nezačíná pomlčkou a řádek 10 je nejdůležitější, protože volá příkaz compgen, který dokáže vygenerovat ze zadaných možností seznam nabízených hodnot. ten funguje následovně.

$ compgen -W "--foo --bar" -- --f
--foo
$ compgen -W "--foo --bar" -- --
--foo
--bar

Po vytvoření příkazu foo (a aktivaci doplňování) je potom možné psát

$ foo [TAB]
--ham   --spam
$ foo --h[TAB]
$ foo --ham

Související články

Anketa: Jaký je váš výchozí shell?
Seriál: BASH

Odkazy a zdroje

info bash
Bash reference manual
An introduction to bash completion: part 1
An introduction to bash completion: part 2

Další články z této rubriky

Praktický test komprese ZPAQ v programu lrzip
Porovnávání souborů PDF
Microsoft rozdává zadarmo stovky e-knih
Minimalistické prezentace s Markdown
Kde hledat Creative Commons a alternativy

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.