8.8 可编程完成的例子
在complete
和compgen
提供的默认动作之外,获得额外完成功能的最常见的方法是使用一个shell函数,并使用complete -F
将其绑定到一个特定的命令上。
下面的函数为cd
内置函数提供了补全功能。 它是一个合理的例子,说明shell函数在用于补全时必须做什么。这个函数使用作为$2
传递的单词来确定要完成的目录名。你也可以使用COMP_WORDS
数组变量;当前单词由COMP_CWORD
变量索引。
这个函数依靠complete
和compgen
内置函数来完成大部分工作,只增加了Bash cd
在接受基本目录名之外的事情:波浪号扩展(见2 波浪号扩展),搜索$CDPATH中的目录,这在上面有描述(见4.1 Bourne Shell内置程序),以及对cdable_vars
shell选项的基本支持(见2 Shopt 内置程序)。_comp_cd
修改了IFS的值,使其只包含一个换行,以适应包含空格和制表符的文件名 –compgen
将其生成的可能的补语每行打印一次。
可能的完成度进入COMPREPLY数组变量,每个数组元素一个完成度。当函数返回时,可编程的完成度系统从那里检索完成度。
# A completion function for the cd builtin
# based on the cd completion function from the bash_completion package
_comp_cd()
{
local IFS=$' \t\n' # normalize IFS
local cur _skipdot _cdpath
local i j k
# Tilde expansion, which also expands tilde to full pathname
case "$2" in
\~*) eval cur="$2" ;;
*) cur=$2 ;;
esac
# no cdpath or absolute pathname -- straight directory completion
if [[ -z "${CDPATH:-}" ]] || [[ "$cur" == @(./*|../*|/*) ]]; then
# compgen prints paths one per line; could also use while loop
IFS=$'\n'
COMPREPLY=( $(compgen -d -- "$cur") )
IFS=$' \t\n'
# CDPATH+directories in the current directory if not in CDPATH
else
IFS=$'\n'
_skipdot=false
# preprocess CDPATH to convert null directory names to .
_cdpath=${CDPATH/#:/.:}
_cdpath=${_cdpath//::/:.:}
_cdpath=${_cdpath/%:/:.}
for i in ${_cdpath//:/$'\n'}; do
if [[ $i -ef . ]]; then _skipdot=true; fi
k="${#COMPREPLY[@]}"
for j in $( compgen -d -- "$i/$cur" ); do
COMPREPLY[k++]=${j#$i/} # cut off directory
done
done
$_skipdot || COMPREPLY+=( $(compgen -d -- "$cur") )
IFS=$' \t\n'
fi
# variable names if appropriate shell option set and no completions
if shopt -q cdable_vars && [[ ${#COMPREPLY[@]} -eq 0 ]]; then
COMPREPLY=( $(compgen -v -- "$cur") )
fi
return 0
}
我们使用-F选项向complete
安装完成功能。
# Tell readline to quote appropriate and append slashes to directories;
# use the bash default completion for other arguments
complete -o filenames -o nospace -o bashdefault -F _comp_cd cd
由于我们希望Bash和Readline为我们处理一些其他的细节,我们使用了其他几个选项来告诉Bash和Readline该怎么做。-o filenames选项告诉Readline,可能的补语应该被视为文件名,并被适当引用。该选项还将导致Readline对它能确定为目录的文件名附加一个斜线(这就是为什么我们可能想要扩展_comp_cd
来附加一个斜线,如果我们使用通过CDPATH找到的目录。-o nospace 选项告诉Readline不要在目录名上附加一个空格字符,以防我们想附加进去。 -o bashdefault 选项带来了其余的"Bash default" completions – Bash添加到默认Readline集的可能的完成方式。这些包括诸如命令名补全、以‘$’或‘${’开头的变量补全、包含路径名扩展模式的补全(参见8 文件名扩展),等等。
一旦使用complete
安装,每当我们试图为cd
命令完成单词时,_comp_cd
就会被调用。
还有更多的例子–大部分常见的GNU、Unix和Linux命令的补全集合–可作为bash_completion项目的一部分。许多GNU/Linux发行版都默认安装了这个项目。该项目最初由Ian Macdonald编写,现在在https://github.com/scop/bash-completion/。还有用于其他系统的端口,如Solaris和Mac OS X。
bash_completion包的旧版本与bash一起发布在examples/complete子目录下。