shell最简单脚本
linux脚本提⽰,Shell脚本中实现⾃动补全功能
对于Linuxer来说,⾃动补全是再熟悉不过的⼀个功能了。当你在命令⾏敲下部分的命令时,肯定会本能地按下Tab键补全完整的命令,当然除了命令补全之外,还有⽂件名补全。
Bash-completion
⾃动补全这个功能是Bash⾃带的,但⼀般我们会安ash-completion包来得到更好的补全效果,这个包提供了⼀些现成的命令补全脚本,⼀些基础的
bash-completion这个包的安装位置因不同的发⾏版会有所区别,但是⼤致上启⽤的原理是类似的,⼀般会有⼀个名为bash_completion 的脚本,这个脚本会在
1 # Check for interactive bash and that we haven't already been sourced.
2 [ -z "$BASH_VERSION" -o -z "$PS1" -o -n "$BASH_COMPLETION" ] && return
3
4 # Check for recent enough version of bash.
5 bash=${BASH_VERSION%.*};
bmajor=${bash%.*}; bminor=${bash#*.}6 if [ $bmajor -gt 3 ] || [ $bmajor -eq 3 -a $bminor -ge 2 ]; then7 if shopt -q progcomp && [ -r /etc/bash_completion ]; then8 # Source completion code.9 . /etc/bash_completion10 fi11 fi12 unset bash bmajor bminor
⽽在bash_completion脚本中会加载/
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内置有两个补全命令,分别是compgen和complete。compgen命令根据不同的参数,⽣成匹配单词的候选补全列表,例如:
$ compgen -W 'hi hello how world' hhihellohow
compgen最常⽤的选项是-W,通过-W参数指定空格分隔的单词列表。h即我们在命令⾏当前键⼊的单词,执⾏完后会输出候选的匹配列表,这⾥是以h开头的所有单词。
complete命令的参数有点类似compgen,不过它的作⽤是说明命令如何进⾏补全,例如同样使⽤-W参数指定候选的
$ complete -W 'word1 word2 word3 hello' foo$ foo w$ foo wordword1 word2 word3
我们还可以通过-F参数指定⼀个补全函数:
$ complete -F _foo foo
现在键⼊foo命令后,会调⽤_foo函数来⽣成补全的列表,完成补全的功能,这⼀点正是补全脚本实现的关键所在,我们会在后⾯介绍。
补全相关的内置变量
除了上⾯的两个补全命令外,Bash还有⼏个内置的变量⽤来辅助补全功能,这⾥主要介绍其中三个:COMP_WORDS: 类型为数组,存放当前命令⾏中输⼊的所有单词;
COMP_CWORD: 类型为整数,当前光标下输⼊的单词位于COMP_WORDS数组中的索引;
COMPREPLY: 类型为数组,候选的补全结果;
COMP_WORDBREAKS: 类型为字符串,表⽰
COMP_LINE: 类型为字符串,表⽰当前的命令⾏输⼊;
例如我们定义这样⼀个补全
$ 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 bdeclare -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表⽰当前光标下的
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 --filea.sh b.sh c.sh
安装补全脚本
如果安装了Bash-completion包,可以将补全脚本放在/