为自己的Linux程序提供二级自动补全功能

为自己的Linux程序提供二级自动补全功能

如果有想法/建议欢迎在评论区中提出, 一同进步, 共勉

在 Linux 中, 通常可以使用Tab键对当前已经输入的内容进行自动的补全操作, 但是这个操作对于自己的脚本一般只能抵达第一级补全, 也就是补全工具的名字, 不能补全自己脚本的第二级及后续的参数. 这个功能实际上是需要我们自己来实现的, 本文将引导您使用complete/compctl为自己的Linux程序提供二级自动补全功能.

工具介绍

  • bash: complete是bash的一个内建命令, 通过它, 我们能为bash建立钩子以响应用户的Tab事件.
  • zsh: compctl是zsh的一个内建命令, 通过它, 我们能为zsh建立钩子以响应用户的Tab事件.

预先的准备, 环境与示范工具等

因为目标始终要针对一个工具, 我就以minecraftctl为示例, 一步一步的示范如何操作

实现示例

bash-complete

通过使用complete命令, 可以让我们为一个命令前缀绑定上一个自动补全响应函数:

#!/bin/bash
# ~/.bashrc
_minecraftctl() {
  COMPREPLY=();
  # {COMP_WORDS[COMP_CWORD]} 可以获取到用户的输入
  local word="{COMP_WORDS[COMP_CWORD]}";
  # 这里是有关于minecraftctl的特殊实现, 稍后我将以另一个例子来解释这个操作
  # 这里将会产出一个候选词列表, 以IFS为分割
  local completions=`find /opt/minecraftctl/module/ -name "*.sh" -exec basename {} \; | grep -oe "^[a-zA-Z]*"`;
  # 将候选词列表与用户输入的内容进行比对, 匹配到最有可能的词
  COMPREPLY=(`compgen -W "{completions}" -- "{word:-h}"`)
}
# 将 minecraftctl 这个前缀绑定到 _minecraftctl 这个函数
complete -f -F _minecraftctl minecraftctl

zsh-compctl

通过使用compctl命令, 可以让我们为一个命令前缀绑定上一个自动补全响应函数:

#!/bin/bash
# ~/.zshrc
_minecraftctl() {
  # {1} 可以获取到用户对应位置的输入
  local word="1";
  # 这里是有关于minecraftctl的特殊实现, 稍后我将以另一个例子来解释这个操作
  # 这里将会产出一个候选词列表, 以换行符为分割
  local completions=`find /opt/minecraftctl/module/ -name "*.sh" -exec basename {} \; | grep -oe "^[a-zA-Z]*"`;
  # 将候选词列表与用户输入的内容进行比对, 匹配到最有可能的词
  reply=("${(ps:\n:)completions}");
}
# 将 minecraftctl 这个前缀绑定到 _minecraftctl 这个函数
compctl -f -K _minecraftctl minecraftctl

讲解与示例

参考上方两个脚本, 我们不难发现, bash与zsh的具体流程区别并不大, 都是为命令前缀绑定钩子函数, 获取用户输入的内容, 对已有的候选列表进行匹配后返回给用户.

在本文中, 我们来一步一步的解析这个流程.

第一步: 注册钩子

# bash
complete -f -F _minecraftctl minecraftctl
# zsh
compctl -f -K _minecraftctl minecraftctl

由于两个shell使用的工具不同, 参数不同也是很正常的事情, 这两个工具中最重要的部分就是将_minecraftctl这个函数注册为了minecraftctl这个命令的自动补全响应函数(这个过程也叫做注册钩子), 这样子当用户按下Tab时就会调用这个函数了

第二步: 输入与输出

此函数最大的功能就是处理输入的数据返回输出的数据, 但是由于钩子的特殊性, 输入并不一定是以参数的形式(zsh是), 输出也不是传统的return或者echo的形式, 而是以预设的变量进行返回.

获取输入

# fun _minecraftctl@bash
local word="{COMP_WORDS[COMP_CWORD]}";
# fun _minecraftctl@zsh
local word="1";

这一步中, 我们可以在函数 _minecraftctl 中获取到用户当前输入到一半的二级参数, 例如, 用户如果输入到 minecraftctl h , 那么 ${COMP_WORDS[COMP_CWORD]}$1 获取到的就是h

这里的 $1 也是可以换成 $2 之类的获取其他级的内容.

COMP_WORDS 这个变量则是一个特殊变量, 具体可参照文末参考文章 2

返回输出

# bash
COMPREPLY=(`compgen -W "{completions}" -- "{word:-h}"`)
#zsh
reply=("${(ps:\n:)completions}")

在这一步中, 两个shell有相当大的区别

  • bash: 要求通过变量COMPREPLY返回一组确切的候选项(以换行符分割), 随着用户按Tab会依次选择
  • zsh: 要求通过变量reply返回所有候选项(以空格分割), 会根据用户的输入依次显示匹配并选择

在这里附上对两个命令的简单注释, compgen 是一个比对工具, 它从 completions 里找到最有可能和 word 匹配的内容, 以换行符作为分隔符列出

而那句 ${(ps:\n:)completions} 的意思呢, 就是吧一个以换行符分割的字符串转为以空格分割的字符串(就是把\n换成空格)

第三步: 中间处理

local completions=`find /opt/minecraftctl/module/ -name "*.sh" -exec basename {} \; | grep -oe "^[a-zA-Z]*"`;

在这里我们获取一个候选词列表, 由于这个实现是minecraftctl自己的实现, 我在这里展示一下会显示的内容:

$ find /opt/minecraftctl/module/ -name "*.sh" -exec basename {} \; | grep -oe "^[a-zA-Z]*"
edit
join
help
install
listen
backup
say
stop
QQMsg
view
restart
download
start

这里是取得一个以换行符为分割的候选词列表

总结

实际上创建此钩子并不难, 主要是需要啃文档

参考文章

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇