2021-05-12 14:32:11
命令列自動補全原理
命令列自動補全原理
- 概述
- 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 的機制更加易於維護。
本文永久更新連結地址:http://www.linuxidc.com/Linux/2016-10/136201.htm
相關文章