大家都知道 bash
里有个文件自动补全功能,按两下 Tab 键显示出候选补全文件名,不同程序会显示相应的文件类型(根据扩展名来判断),例如
mplayer 会显示媒体文件(如 avi、mpg、mkv),而不会显示文本文件(如 txt、cfg、ini),而 Vim
则是反过来,这样的一直以来相当好用,不过还是有些时候还是挺烦人的。
有时我想用 Vim 的 diff 功能来比较文件,比如两个 java 的
class 文件,虽然 class 文件是二进制文件,但在 Vim 中可以转换成十六进制来显示,方便比较。打了 Vim
命令后,按两下 Tab 并补全候选不会有
class 文件,所以你得手动输入。
mplayer
的一个参数 -ass-styles,这个参数用于使用外部
ass 字幕的样式,后面接一个 ass 文件的路径,用样也不会显示出来,因为 bash 的自动补全里并没有为这个参数定义过。
这两个例子只是最近用到,其实一直以来也遇过这种情况,很多时候我会把按两次 Tab 来当
ls
命令使用,看看目录下有什么文件,但这样会让某些文件没显示出来,让你误以为文件不存在,当前目录短文件名就直接输入算了,好几层的目录和长文件名就麻烦,对于这种情况,我的土方法是
把打了一半的命令行 Ctrl
+ c 掉,然后用 ls 看列一下,复制粘贴路径出来
或者先直接补全路径后,把光标移回开头输入命令名
这样太不够效率了,尤其是在调试时,命令参数记得比路径还要熟的,干脆关掉这个文件扩展名检测功能好了。
我记得 bash 的补全脚本在
/etc/bash_completion,这个文件好长,稍微看了一下,发现有关联的代码,比如这段看来就是定义补全的扩展名
complete -f -X '!*.fig' xfig
complete -f -X '!*.@(mid?(i)|MID?(I)|cmf|CMF)' playmidi
complete -f -X '!*.@(mid?(i)|MID?(I)|rmi|RMI|rcp|RCP|[gr]36|[GR]36|g18|G18|mod|MOD|xm|XM|it|IT|x3m|X3M|kar|KAR)' timidity
complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' vi vim gvim rvim view rview rgvim rgview gview
complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' emacs
complete -f -X '!*.@(exe|EXE|com|COM|scr|SCR|exe.so)' wine
直接执行 complete 会输出当前定义过补全的命令,看样子也是在
/etc/bash_completion 里定义的
$ complete | grep " mplayer\| vim"
complete -o filenames -F _mplayer mplayer
complete -o filenames -F _filedir_xspec vim
man 了一下 complete 命令,没有手册,属于 builtin
命令,上网找,简介在 这里 ,用法在
大概看了一下用法,就是 complete
补全行为选项 命令名
-F:执行指定函数名,候选结果保存在 COMPREPLY 数组变量里,补全功能更强大,可以实现命令参数补全,函数名在
/etc/bash_completion 定义的。
-f:补全文件名,后可跟 -X 参数。
-X:过滤表达式,符合表达式的文件名会被排除,即不会在补全候选显示出来,如果以感叹号开头,则表示反转,即符合表达式的文件名才显示。
-o:补全类型,filenames 表示补全的是一个文件,跟 -f 参数使用才有效;其它值如 dirnames 表示补全目录。
-F 执行的函数比 -f
-X 更早执行,这样说起来有点混乱,我测试了一下,
当你输入好「命令名 」,按两下Tab键,开始补全。
bash
执行了 -F 指定的函数,函数执行完后输出数组变量 COMPREPLY 的结果,这个变量值有可能为空。
继续根据 -f
-X 里的表达式,符合表达式的文件名不则显示,同时结果再根据 -o 指定补全类型来决定是否显示。
就以 Vim 和 mplayer 举例
对于 Vim 来说,它最终被定义的行为这样
complete -o filenames -F _filedir_xspec -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' vim
先执行 _filedir_xspec 函数,而这个函数执行后变量 COMPREPLY 为空,所以什么也没输出。接着补全文件名,可以看 class 字符串在表达式里面,所以不显示。如果删掉 class| 再运行,就可以匹配
class 文件了。
对于mplayer,它最终被定义的行为这样
complete -o filenames -F _mplayer mplayer
先执行了 _mplayer 函数,这个函数在
/etc/bash_completion 代码非常多,mplayer 的选项参数都打上去了,所以能补全 mplayer
的选项参数。到补全文件名时,没有指定过滤表达式,所以什么都没做。亦即它的补全功能全由 _mplayer 函数完成。
以上结果纯属连猜带蒙,若有错误,烦请指正,为了介绍一个小功能码了这么多字容易嘛我,所以
临时禁用 Vim 的补全功能就是
complete vim
保留路径补全,但不要扩展名检测,不指定「-X」即可
complete -f vim
只补全文件
complete -o filenames -f vim
只补全目录
complete -o dirnames vim