2.6 可编程补全
在熟悉了命令行自动补全的用法之后,如果你是一位开发人员的话,那么或许会问到这样的问题:“如何为自己所写的程序或脚本添加命令补全呢?”利用 bash 和 zsh 提供的可编程补全特性,我们可以方便地对命令补全加以定制。下面我们就从示例出发来一探究竟。
2.6.1 bash 示例
假设我编写的程序名叫 mycmd,它具有 --help 和 --version 两个命令选项。让我们先来看看它的命令补全效果。当我输入
并按 Tab 后,这时 bash 为我呈现了该命令的全部选项列表,同时补全成了 mycmd --。
我接着输入 h,再按 Tab,bash 这次就自动补全了命令选项 --help。啊哈,这正是我想要的命令补全。那么,如何实现可编程补全呢?
首先,我们需要在 /etc/bash_completion.d 目录下创建 mycmd 文件(亦即 /etc/bash_completion.d/mycmd)。这样,bash 就会自动加载我们在 mycmd 中编写的补全代码。
其次,我们在 mycmd 中编写如下用于处理命令自动补全的代码。如图 2.12 所示。
#
# Completion for mycmd
#
_mycmd() {
local cur opts
cur="${COMP_WORDS[COMP_CWORD]}"
opts="--help --version"
if [[ ${cur} == -* ]]; then
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
return 0
fi
}
complete -F _mycmd mycmd
图 2.12: bash 可编程补全示例
这是一个典型的 bash 脚本。开头的 # 为注释,用于说明补全的用途。
接着我们定义了一个名为 _mycmd 的函数,该函数包含用来处理 mycmd 命令的选项的逻辑。
local 声明了两个变量:cur 和 opts。其中,cur 存储当前在命令行正输入的字,它通过 bash 内置的变量 COMP_WORDS 和 COMP_CWORD 获取。
COMP_WORDS:数组变量,包含当前命令行中单独的字。COMP_CWORD:表示当前光标位置在${COMP_WORDS}中的索引。
而 opts 则用来保存 mycmd 命令所有的命令选项。
然后,我们判断 $cur 是否为 - 打头,若为真,那么就用 compgen 命令来生成可供补全的选项列表。-W 选项后跟我们需要的 mycmd 命令选项。
与此同时,我们将 compgen 产生的输出赋给又一个 bash 内置变量 COMPREPLY。这样,当需要补全时,bash 就会采用 compgen 生成的补全列表了。
最后,我们用 complete 将补全函数 _mycmd(-F 选项)与程序 mycmd 绑定在一起即可。
2.6.2 zsh 示例
现在,让我们来看看在 zsh 中又怎么实现可编程补全吧。
假如我们把 mycmd 的补全代码保存到 $HOME/.zsh/_mycmd 中的话,那么需要在 $HOME/.zshrc 里设置 $fpath,以便 zsh 能够加载我们的补全代码。
下面就是我们针对 zsh 而改写的 mycmd 自动补全代码。如图 2.13 所示。
#compdef mycmd
#
# Completion for mycmd
#
_mycmd() {
local cur opts
cur="${words[CURRENT]}"
opts=(--help --version)
if [[ ${cur} == -* ]]; then
compadd -- ${opts}
return 0
fi
}
_mycmd "$@"
图 2.13: zsh 可编程补全示例
第一行的注释并非普通注释(#compdef mycmd),它允许 zsh 为我们自动载入补全代码。
接下来定义的函数与变量跟 bash 示例相似,其中已经替换成 zsh 里等价的内容。
words相当于 bash 中的COMP_WORDSCURRENT与 bash 中的COMP_CWORD类似COMPREPLY则和compadd这个内置的 zsh 命令相同
要试验 mycmd 在 zsh 中的补全效果,只需先执行一下 source ~/.zshrc。从下面的例子中,你可以看到 mycmd 的命令补全跟 bash 中几乎一样,当然也带着 zsh 原本的补全功能。
值得一提的是,zsh 本身还提供了一些辅助函数以用于补全,比如 _arguments、_describe、_message 等等,各位读者诸君不妨参考 zsh 的官方文档详加了解,以便用到自己的补全代码中。