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
这是一个典型的 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 "$@"
第一行的注释并非普通注释(#compdef mycmd
),它允许 zsh 为我们自动载入补全代码。
接下来定义的函数与变量跟 bash 示例相似,其中已经替换成 zsh 里等价的内容。
words
相当于 bash 中的COMP_WORDS
CURRENT
与 bash 中的COMP_CWORD
类似COMPREPLY
则和compadd
这个内置的 zsh 命令相同
要试验 mycmd
在 zsh 中的补全效果,只需先执行一下 source ~/.zshrc
。从下面的例子中,你可以看到 mycmd
的命令补全跟 bash 中几乎一样,当然也带着 zsh 原本的补全功能。
值得一提的是,zsh 本身还提供了一些辅助函数以用于补全,比如 _arguments
、_describe
、_message
等等,各位读者诸君不妨参考 zsh 的官方文档详加了解,以便用到自己的补全代码中。