歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> 命令行自動補全原理

命令行自動補全原理

日期:2017/2/28 13:48:00   编辑:Linux教程

命令行自動補全原理

  • 概述
  • bash 自動補全
  • 測試補全的腳本
  • 參數自動補全
  • 自定義補全
  • zsh 自動補全
  • 參數自動補全
  • 自定義補全
  • 總結

概述

雖然CLI(命令行)類型的工具由於其高效,易定制的特性為很多人所喜愛(也包括我自己), 但是,相對於GUI工具,CLI工具給人的直觀感覺就是不容易使用,如果看到工具中大量的參數說明後,更讓人望而卻步。

因此,如果在自己命令行工具中加入 自動補全 的功能,就可以極大的提高工具的易用性,還可以保留命令行工具原有的高效。 這裡所說的 自動補全 不僅僅是補全那些固定的參數(這些意義不大),更多的是補全動態的內容。

本篇主要介紹兩種主流的 shell(bash 和 zsh)中,如何實現命令行工具的補全。

bash 自動補全

測試補全的腳本

簡單編寫一個測試腳本用來測試後面的自動補全:

#!/bin/bash
# filename: cli-test.sh

UPCASE=false
DATE=""

usage() {
    echo "USAGE:"
    echo "cli-test <options>"
    echo "    -h      : print help"
    echo "    -u      : print info upcase"
    echo "    -p <xxx>: print info"
    echo "    -d <xxx>: date in print info"
}

print() {
    if $UPCASE
    then
       echo -n $1 | tr a-z A-Z
    else
        echo -n $1
    fi

    if [ "$DATE" != "" ]
    then
        echo "   date: $DATE"
    else
        echo ""
    fi
}

while getopts "hup:d:" opt; do
    case "$opt" in
        h)
            usage
            exit 0
            ;;
        u)
            UPCASE=true
            ;;
        d)
            DATE=$OPTARG
            ;;
        p)
            print $OPTARG
            ;;
    esac
done

測試上面的腳本如下:

bash-3.2$ chmod +x cli-test.sh
bash-3.2$ ./cli-test.sh -h
USAGE:
cli-test <options>
    -h      : print help
    -u      : print info upcase
    -p <xxx>: print info
    -d <xxx>: date in print info
bash-3.2$ ./cli-test.sh -p hello
hellobash-3.2$ ./cli-test.sh -p hello
hello
bash-3.2$ ./cli-test.sh -u -p hello
HELLO
bash-3.2$ ./cli-test.sh -u -d 2016-10-13 -p hello
HELLO   date: 2016-10-13

參數自動補全

參數的補全一般來說比較簡單,因為一個命令行工具的參數一般都是固定的。 下面的參數補全腳本是針對 上面的 測試補全的腳本 cli-test.sh

_complete_func() {
    local cur prev opts base
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    opts="-h -u -d -p"

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

}

complete -F _complete_func cli-test.sh

讓自動補全腳本生效的方法如下:

bash-3.2$ source bash_complete          # 使自動補全腳本生效
bash-3.2$ ./cli-test.sh -<TAB><TAB>     # 這裡輸入 - 之後,再輸入2次<TAB>就可以把所有能補全的參數列出來

自定義補全

上面的補全是補全固定的參數,簡單,但是用處也不大,用戶記不住的其實就是那些會變的參數內容。 下面嘗試動態補全 cli-test.sh 的參數 -d 的內容(內容是當前日期以及前3天和後三天的日期) 修改 bash_complete 腳本如下:

_complete_func() {
    local cur prev opts base
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    if [[ ${cur} == -* ]] ; then
        opts="-h -u -d -p"
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
    else
        opts=$( _complete_d_option )
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
    fi

    return 0
}

_complete_d_option() {
    date -v -3d +"%Y-%m-%d"
    date -v -2d +"%Y-%m-%d"
    date -v -1d +"%Y-%m-%d"
    date +"%Y-%m-%d"
    date -v +1d +"%Y-%m-%d"
    date -v +2d +"%Y-%m-%d"
    date -v +3d +"%Y-%m-%d"
}

complete -F _complete_func cli-test.sh

測試動態補全的效果

bash-3.2$ source bash_complete          # 使自動補全腳本生效
bash-3.2$ ./cli-test.sh -u -d 2016-10-1<TAB><TAB>   # 這是 2016-10-13 執行的結果,其他日子的結果會不一樣
2016-10-10  2016-10-11  2016-10-12  2016-10-13  2016-10-14  2016-10-15  2016-10-16

上面就是動態補全,_complete_d_option 函數就是用來實現動態補全的。

zsh 自動補全

相比於bash,zsh 的補全機制更加強大,也更加直觀。 同樣,下面也通過例子來演示如何在 zsh 中實現上面 bash 中同樣的補全功能。

參數自動補全

相比於 bash 的自動補全腳本,我覺得 zsh 的補全方式更加直觀。

#compdef cli-test.sh
# filename: _cli-test.h

_cli_test() {

    _arguments -C -s -S \
               '-h::' \
               '-u::' \
               '-d::' \
               '-p::'
}

_cli_test "$@"

zsh 中有個 fpath 的內置變量,將自動補全腳本放在 $fpath 中,或者在 $fpath 中創建指向自動補全的腳本的軟連接都可以。 下面是我的環境中 fpath 的值

$ echo $fpath
/usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/5.0.8/functions

為了測試 zsh 下自動補全是否有效,我在 fpath 下給自己的自動補全腳本創建了軟連接

$ cd /usr/local/share/zsh/site-functions
$ ln -s ~/projects/bash/autocomp/_cli-test.sh _cli-test.sh

測試結果

$ ./cli-test.sh -<TAB><TAB>
-d  -h  -p  -u

可以看出,zsh 的補全方法非常簡單直觀。稍微解釋下上面的代碼

_arguments

這個函數是 zsh 自帶的,有點類似 bash 中的 compgen ,但是功能更加強大。

'-h::' \

這裡 : 分割的3部分分別是 “待補全的參數:參數的說明:動態補全參數的內容“

自定義補全

根據上面的解釋,要想動態補全 -d 參數非常簡單,只要加個函數,並配置在 -d:: 之後即可

#compdef cli-test.sh
# filename: _cli-test.h

_cli_test() {

    _arguments -C -s -S \
               '-h::' \
               '-u::' \
               '-d:auto complete date:__complete_d_option' \
               '-p::'
}

__complete_d_option() {
    local expl
    dates=( `generate_date` )

    _wanted dates expl date compadd $* - $dates
}

generate_date() {
    date -v -3d +"%Y-%m-%d"
    date -v -2d +"%Y-%m-%d"
    date -v -1d +"%Y-%m-%d"
    date +"%Y-%m-%d"
    date -v +1d +"%Y-%m-%d"
    date -v +2d +"%Y-%m-%d"
    date -v +3d +"%Y-%m-%d"
}

_cli_test "$@"

測試動態補全的效果

$ ./cli-test.sh -u -d 2016-10-<TAB><TAB>
2016-10-14  2016-10-15  2016-10-16  2016-10-17  2016-10-18  2016-10-19  2016-10-20

總結

2中shell環境下的自動補全都介紹完了,它們自動補全的機制都不難,只是 zsh 畢竟是新一點的shell,補全方式更加簡單易懂。 特別是對於存在子命令和復雜的參數補全,以及參數內容動態補全的情況下,zsh 的機制更加易於維護。

Copyright © Linux教程網 All Rights Reserved