編寫 Bash 補全腳本

轉載自: 糰子的小窩 , 本文固定鏈接: 編寫 Bash 補全腳本

對於Linuxer來說,自動補全是再熟悉不過的一個功能了。當你在命令行敲下部分的命令時,肯定會本能地按下Tab鍵補全完整的命令,當然除了命令補全之外,還有文件名補全。

Bash-completion

自動補全這個功能是Bash自帶的,但一般我們會安裝bash-completion包來得到更好的補全效果,這個包提供了一些現成的命令補全腳本,一些基礎的函數方便編寫補全腳本,還有一個基本的配置腳本。但也正如之前說的,這個包不是必須的,只不過可以省些力氣。

bash-completion這個包的安裝位置因不同的發行版會有所區別,但是大致上啓用的原理是類似的,一般會有一個名爲bash_completion的腳本,這個腳本會在shell初始化時加載。例如對於RHEL系統來說,這個腳本位於/etc/bash_completion,而該腳本會由/etc/profile.d/bash_completion.sh中導入:

# Check for interactive bash and that we haven't already been sourced.[ -z "$BASH_VERSION" -o -z "$PS1" -o -n "$BASH_COMPLETION" ] && return# Check for recent enough version of bash.bash=${BASH_VERSION%.*}; bmajor=${bash%.*}; bminor=${bash#*.}if [ $bmajor -gt 3 ] || [ $bmajor -eq 3 -a $bminor -ge 2 ]; then
    if shopt -q progcomp && [ -r /etc/bash_completion ]; then
        # Source completion code.
        . /etc/bash_completion    fifiunset bash bmajor bminor

而在bash_completion腳本中會加載/etc/bash_completion.d下面的補全腳本:

if [[ $BASH_COMPLETION_DIR != $BASH_COMPLETION_COMPAT_DIR && \    -d $BASH_COMPLETION_DIR && -r $BASH_COMPLETION_DIR && \    -x $BASH_COMPLETION_DIR ]]; then
    for i in $(LC_ALL=C command ls "$BASH_COMPLETION_DIR"); do
        i=$BASH_COMPLETION_DIR/$i        [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|*.rpm@(orig|new|save)|Makefile*) \
            && -f $i && -r $i ]] && . "$i"
    donefiunset i

補全腳本的名稱一般就是命令名,這樣比較容易查找:

$ ls i*iconv  iftop  ifupdown  info  iproute2  iptables

內置補全命令

Bash內置有兩個補全命令,分別是compgencompletecompgen命令根據不同的參數,生成匹配單詞的候選補全列表,例如:

$ compgen -W 'hi hello how world' h
hi
hello
how

compgen最常用的選項是-W,通過-W參數指定空格分隔的單詞列表。h即我們在命令行當前鍵入的單詞,執行完後會輸出候選的匹配列表,這裏是以h開頭的所有單詞。

complete命令的參數有點類似compgen,不過它的作用是說明命令如何進行補全,例如同樣使用-W參數指定候選的單詞列表:

$ complete -W 'word1 word2 word3 hello' foo
$ foo w<Tab>$ foo word<Tab>word1  word2  word3

我們還可以通過-F參數指定一個補全函數:

$ complete -F _foo foo

現在鍵入foo命令後,會調用_foo函數來生成補全的列表,完成補全的功能,這一點正是補全腳本實現的關鍵所在,我們會在後面介紹。

補全相關的內置變量

除了上面的兩個補全命令外,Bash還有幾個內置的變量用來輔助補全功能,這裏主要介紹其中三個:

  • COMP_WORDS: 類型爲數組,存放當前命令行中輸入的所有單詞;

  • COMP_CWORD: 類型爲整數,當前光標下輸入的單詞位於COMP_WORDS數組中的索引;

  • COMPREPLY: 類型爲數組,候選的補全結果;

  • COMP_WORDBREAKS: 類型爲字符串,表示單詞之間的分隔符;

  • COMP_LINE: 類型爲字符串,表示當前的命令行輸入;

例如我們定義這樣一個補全函數_foo:

$ function _foo()> {>     echo -e "\n"> >     declare -p COMP_WORDS>     declare -p COMP_CWORD>     declare -p COMP_LINE>     declare -p COMP_WORDBREAKS> }$ complete -F _foo foo

假設我們在命令行下輸入以下內容,再按下Tab鍵補全:

$ foo b

declare -a COMP_WORDS='([0]="foo" [1]="b")'declare -- COMP_CWORD="1"declare -- COMP_LINE="foo b"declare -- COMP_WORDBREAKS=" 	
\"'><=;|&(:"

對着上面的結果,我想應該比較容易理解這幾個變量。當然正如我們之前據說,Bash-completion包並非是必須的,補全功能是Bash自帶的。

編寫腳本

補全腳本分成兩個部分:編寫一個補全函數和使用complete命令應用補全函數。後者的難度幾乎忽略不計,重點在如何寫好補全函數。難點在,似乎網上很少與此相關的文檔,但是事實上,Bash-completion自帶的補全腳本是最好的起點,可以挑幾個簡單的改改基本上就可以使用了。

一般補全函數(假設這裏依然爲_foo)都會定義以下兩個變量:

local cur prev

其中cur表示當前光標下的單詞,而prev則對應上一個單詞:

cur="${COMP_WORDS[COMP_CWORD]}"prev="${COMP_WORDS[COMP_CWORD-1]}"

初始化相應的變量後,我們需要定義補全行爲,即輸入什麼的情況下補全什麼內容,例如當輸入-開頭的選項的時候,我們將所有的選項作爲候選的補全結果:

local opts="-h --help -f --file -o --output"if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0fi

不過再給COMPREPLY賦值之前,最好將它重置清空,避免被其它補全函數干擾。

現在完整的補全函數是這樣的:

function _foo() {
    local cur prev opts

    COMPREPLY=()

    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="-h --help -f --file -o --output"

    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi}

現在在命令行下就可以對foo命令進行參數補全了:

$ complete -F _foo foo
$ foo --f        --file    -h        --help    -o        --output

當然,似乎我們這裏的例子沒有用到prev變量。用好prev變量可以讓補全的結果更加完整,例如當輸入--file之後,我們希望補全特殊的文件(假設以.sh結尾的文件):

    case "${prev}" in
        -f|--file)
            COMPREPLY=( $(compgen -o filenames -W "`ls *.sh`" -- ${cur}) )
            ;;
    esac

現在再執行foo命令,--file參數的值也可以補全了:

$ foo --file<Tab>a.sh b.sh c.sh

安裝補全腳本

如果安裝了Bash-completion包,可以將補全腳本放在/etc/bash_completion.d目錄下,或者放到~/.bash_completion文件中。
如果沒有安裝Bash-completion包,可以把補全腳本放到~/.bashrc或者其它能被shell加載的初始化文件中。

參考鏈接

  1. An introduction to bash completion: part 2

  2. Writing your own Bash Completion Function

  3. Programmable Completion

  4. Completion Files


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章