跟我一起写shell补全脚本(Zsh篇)

栏目: Linux · 发布时间: 6年前

内容简介:绝大部分日常使用Linux和OS X的程序员都会选择zsh作为自己的shell环境,毕竟对比于bash,zsh的便利性/可玩性要胜出很多,同时它又能兼容bash大多数的语法。不过相对而言,zsh补全脚本要比bash补全脚本要难写。

绝大部分日常使用 Linux 和OS X的 程序员 都会选择zsh作为自己的 shell 环境,毕竟对比于bash,zsh的便利性/可玩性要胜出很多,同时它又能兼容bash大多数的语法。不过相对而言,zsh补全脚本要比bash补全脚本要难写。zsh提供了非常多的补全的API,而且这些API功能有不少重叠的地方,掌握起来并不容易。不像bash,你只需记住三个API(compgencompletecompopt)就能实现整个补全脚本。

这篇的任务跟上一篇的一样,需要实现一个针对pandoc的补全脚本,囊括下面三个目标:

  1. 支持主选项(General options)
  2. 支持子选项(Reader options/General writer options)
  3. 支持给选项提供参数值来源

何处安放脚本

在开始之前,需要说明下放置zsh脚本的地方,这样我们才能让接下来写的补全脚本发挥效力。
zsh在启动时会加载$fpath路径下的脚本文件。试试echo $fpath来看看这个变量的值。接下来我们可以把补全脚本放到$fpath的路径下,或者创建一个新的在$fpath路径中的目录:

  1. mkdir ~/.fpath
  2. ~/.zshrc中添加fpath=($HOME/.fpath $fpath)
  3. 重启zsh

当我们把自己写的补全脚本放好后,每次zsh一启动,就会加载它。不过总不能每次修改完脚本后,都重启一次zsh吧。如果只是单纯更新补全脚本,可以执行unfunction _pandoc && autoload -U _pandoc,zsh就会重新加载补全脚本了。(其中_pandoc是补全脚本的名字)

支持主选项

还是跟上一篇一样,先解释一个实现第一个目标的程序,带各位入门:

就像bash的complete,zsh也有一个相对的表示补全的API,就是compdef。zsh补全脚本以#compdef tools开头,表示该文件是针对tools的补全脚本。当然你也可以像bash一样,直接compdef _function tools来指定tools的补全函数。

zsh补全API的第一梯队是_alternative_arguments_describe_gnu_generic_regex_arguments。它们直接提供补全的来源。这些API的概述见https://github.com/zsh-users/zsh-completions/blob/master/zsh-completio…。由于_describe能做的_arguments也能做,_gnu_generic是为GNU拓展的命令参数准备的,_regex_arguments就是正则匹配版的_arguments,所以只要记住_arguments_alternative就够用了。

_arguments接受一连串的选项字符串,每个字符串代表一个选项。另外你还可以通过一些选项指定补全上的细节。举-s为例:假设你的 工具 支持-a -b两个选项,也支持-ab的方式来同时指定两个选项。如果没给_arguments提供-s的选项,那么zsh是不会补全出-ab,因为并不存在选项-ab。而提供了-s后,_arguments才允许你在已经输入-a的情况下,补全出-ab

选项字符串的格式是这样的:-x[description]:message:action。你也可以写做{-x,-y}[description]:message:action形式,表示-x-y是等价的写法。

  1. -x是选项的名字
  2. [description]是该选项的描述,可选
  3. message这一项我也不知道是什么意义……不过它是可选的,除非你需要指定action
  4. action用于生成复杂的补全。在这里你可以使用许多补全语法。一个常见的例子是使用辅助函数,比如_files表示补全当前路径下的文件名。详见:
    1. https://github.com/zsh-users/zsh-completions/blob/master/zsh-completio…
    2. https://github.com/zsh-users/zsh-completions/blob/master/zsh-completio…

最后一行'*:files:_files'表示,如果找不到匹配的候选词,就补全文件名。
到目前为止,实现第一阶段目标的脚本所需的知识点已经讲解完毕。

_arguments有一个限制,它要求选项的名字符合某些特殊格式,比如以-+=等字符开头(所以才叫_arguments嘛)。如果你的工具接受addremove之类的子命令,就需要用到_alternative

_alternative支持的选项字符串格式跟_arguments很像,比如

等价于

支持子选项

所谓的支持子选项,就是在某些选项存在的情况下,增加多一些选项。所以,我们所要做的,就是检查当前输入的命令行参数中是否存在某些参数,如果存在,增加新的选项。这一步可以分解成两个步骤,第一个是检查某些参数是否存在,第二个是增加新的选项。

之前写bash补全脚本的时候,是通过遍历某个存储有当前输入的常量数组,来检查某些参数是否存在。在网上搜索一番后,我发现zsh也有同样的常量数组,就叫做words,正好是bash那个的小写哈。那么接下来就是zsh的语法知识了:

这里用到一点zsh特有的下标语法,相当于index()

那么下面是第二步,该怎么修改补全候选列表呢?如果直接用_arguments指定新的补全列表,会覆盖掉前面指定的补全列表。当然也可以把前面的补全列表复制一份,并添加新的选项,用它覆盖掉原来的补全列表。不过这么一来代码就不好看了。

想来zsh应该提供了对应的API的。果不其然,有一个_values可以用来干这事。_values功能跟_arguments差不多,而且它接受的选项列表是添加到原有的选项列表中的,而不是覆盖。所以最后的代码是这样的:

支持给选项提供参数值来源

最后一步是给-f-r这两个选项提供读操作支持的FORMAT参数,给-t-w这两个选项提供写操作支持的FORMAT参数。

在Bash篇的实现中,我们检查上一个词的值,如果它是-f-r,那么对当前词补全读操作的FORMAT参数。对写操作的选项也同理。
在zsh中,我们可以用一个特殊的Action:->VALUE来实现。

->VALUE这样的Action会把$state变量设置成VALUE,接下来靠一个case语句块就能根据当前陷入的状态进行对应的参数补全。

那么该如何补全FORMAT参数列表呢?这里可以用上_multi_parts
_multi_parts第一个参数是分隔符,之后接受一组候选词或一个候选词数组作为候选词列表。例如_multi_parts , a,b,c,就会生成a b c这个补全候选列表。

这里的FORMAT变量直接使用上一章的$READ_FORMAT$WRITE_FORMAT
我试了一下,如果把FORMAT变量当做字符串传递过去的话,其间的空格会被转义,导致无法分隔开来,于是就把它们改写成数组的形式。

另外,由于补全FORMAT参数时,不再需要补全选项了。所以把补全FORMAT参数的部分提到补全子选项的前面,并在补全后直接退出程序的执行。

最终完成的代码如下:

后话

由于zsh的补全功能实在强大,而这篇文章只是简略地讲讲如何写出一个zsh补全脚本,有许多zsh的补全机制都没能提到。所以补充一些写zsh补全脚本的资料,如果对这方面有兴趣可以继续跳坑:

  1. zsh-completions项目上的教程。这是我见过的最详尽的zsh补全脚本教程。
  2. 官方文档
  3. /usr/share/zsh/functions/Completion 也许你能从相似的命令的补全脚本中汲取灵感。

顺便一提,在查找资料的时候发现有人写了一个完整的pandoc的zsh补全脚本,感兴趣的话可以看一下:
https://github.com/srijanshetty/zsh-pandoc-completion/blob/master/_pan…


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

你凭什么做好互联网

你凭什么做好互联网

曹政 / 中国友谊出版公司 / 2016-12 / 42.00元

为什么有人可以预见商机、超越景气,在不确定环境下表现更出色? 在规则之外,做好互联网,还有哪些关键秘诀? 当环境不给机会,你靠什么翻身? 本书为“互联网百晓生”曹政20多年互联网经验的总结,以严谨的逻辑思维分析个人与企业在互联网发展中的一些错误思想及做法,并给出正确解法。 从技术到商业如何实现,每个发展阶段需要匹配哪些能力、分解哪些目标、落实哪些策略都一一点出,并在......一起来看看 《你凭什么做好互联网》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器