11/20/2019 命令行开发 - 提醒与自动补全实践

0x00 命令行工具的自动补全

开发一个命令行工具不难,完善命令行工具的周边设施,才是迈向专业命令行开发的关键。

而今天提到的 命令行自动补全,就是命令行工具提升体验的重中之重。

比如,当我们输入 git 并按下键盘上的 tab,我们会得到多达 131条的命令提醒。

这当然对我们使用毫无帮助,但是如果我们输入 git che 并按下 tab,我们就能得到一个十分愉悦的反馈。

0x01 命令开发好了,却总是忘记怎么用

这一节,我们从一个已经开发号的命令入手,一步一步实现自动补全。

正如我们看到的,这个临时开发出来的hello命令,并不具备自动补全。

我们期望当输入完hello并按下tab,可以得到提醒并快速地选择chengdufreeCodeCampmohism中的一个。

同时,我们期望当我们输入hello c并按下tab,无需更多的选择,chengdu 会被自动选择,并且填充到输入的末尾。

0x10 如何让命令拥有自动补全的功能

开始之前,提示一下
1. 下面动图里关于闪烁以及突然补全,都是因为我按了 tab
2. 下面列出来的所有 shell 脚本,默认都是保存到一个文件里,并且在恰当的时候执行source让它生效
3. 希望你看得懂第2条

实现自动补全的原理并不复杂,只需要系统内置的 complete命令即可。

我们不妨先手动试试,在命令行输入

complete -W "chengdu freeCodeCamp mohism" hello

简单讲解一下上面的命令,-W 参数,指的是使用文本(字符串)来做补全方式,多个单词使用空格分开。

意思就是,输入hello以后,可以通过tab来提醒并选择chengdu freeCodeCamp mohism中的一个。

如果我们把上面命令加入到我们的 .bashrc里,那每一次打开 SHELL 都能享受到hello命令的自动提醒了。

咦?这么简单就完成了吗??

当然不是的,请看

显然这并不能让我们满意,因为 -W参数只能实现一些简单的功能,更多的情况下,我们会使用 -F,为自动补全指定函数

先奉上完整版:

hello_completions()
{
  #有点长,拖动查看
  if [[ "${COMP_CWORD}" == "1" ]];then
    COMPREPLY=($(compgen -W "chengdu freeCodeCamp mohism" "${COMP_WORDS[1]}"))
  fi
}

complete -F hello_completions hello

上面这个知识点就有点多了,大家可以反复读一下以下五点:

  1. -F的使用,一目了然不需要叙述;
  2. COMPREPLY,这个补全函数内置的,我们需要给它赋值为一个数组, 这里需要一点点 shell 编程能力。COMPREPLY数组最后的元素,就是我们按下tab 后会提示的内容。
  3. compgen是另一个内置命令,compgen -W "abc abd ccc" a 的意思,就是从 abc abd ccc里过滤a开头的元素,得到abc abd
  4. COMP_WORDS是另一个内置变量,它记录了你此时输入的内容。这个比较抽象,当你输入 hello che时,COMP_WORDS[0]就是helloCOMP_WORDS[1]就是che
  5. 还有开头的 COMP_CWORD ,指的是我们正在输入的第几个词。结合第4条,当我们正在 输入第 1个词(hello是第0个),才会执行补全逻辑。

加起来,这个函数的意思就是

我们获取到输入hello之后(排除空格)的第一个输入,比如ch, 然后前缀匹配chengdu freeCodeCamp mohism,过滤后得到的结果(如 chengdu),

作为自动补全的选项(COMPREPLY)。

well ? 看起来跟一开始的没什么不一样。我们再看看效果:

细心的朋友就会发现,我们通过 if [[ "${COMP_CWORD}" == "1" ]]来解决了上面出现过的 无限提醒补全问题

OK,到这里一个基本的自动补全实现方式,就算完成了。更多细节的用法,我还是推荐各位自行搜索更多的资料,加强学习。

0x11 深入:二阶段命令补全

故事当然还没结束,好事者给这个hello命令增加了一些功能

显然,我们被要求对子命令的参数做进一步的提醒与补全。

那就开干吧

hello_completions()
 {
    if [[ "${COMP_CWORD}" == "1" ]];then
      COMPREPLY=($(compgen -W "chengdu freeCodeCamp mohism" ${COMP_WORDS[1]}))
    elif [[ "${COMP_WORDS[1]}" == "mohism" ]];then
      COMPREPLY=($(compgen -W "--color -t" $(t=${COMP_WORDS[${COMP_CWORD}]} && echo ${t//-/})))
    fi
  }

  complete -F _hello_completions hello

原来事情并没有那么复杂,我们只需要再判断第1个输入(COMP_WORDS[1])为 mohism 时,重新定义 COMPREPLY数组的元素即可。

tips: 上面没有讲解的 $(t=${COMP_WORDS[${COMP_CWORD}]} && echo ${t//-/})
就讲给爱学习的人自己去了解吧,把它当作学习 shell 编程的好机会。

此时我们看看完整效果:

0xFF 开箱即用的 @mohism/cli-wrapper

冷不防一个广告。

本人开发的 用来快速开发命令行工具的工具 (拗口?)

@mohism/cli-wrapper

除了能够非常方便快捷地用 Nodejs开发命令行工具外,它还能帮你自动生成支持命令补全的脚本。

所以,如果我们通过 @mohism/cli-wrapper来开发这个 hello命令的话。。。。

谢谢阅读。

侧栏导航