- https://github.com/scop/bash-completion/
本文描述的内容可能因为
bash
版本差异, 执行会有细微差异;
生成集合: 根据拼写一截的补全; 比如
git sta
就有补全stage stash status
;根据sta
过滤;-f -d
生成集合会被FIGNORE
过滤;比如
-G "*.sh"
, 这个就是文件扩张生成当前目录下sh
结尾的文件; 不会被sta
这种影响, 会全部纳入; 但是受到FIGNORE
的影响;
-W "hello world $PATH"
, 进行bash
的扩张后生成的字符串, 再进行bash
分词规则生成字符串集合;git sta
会从过滤集合中过滤前缀sta
的集合;-W
某些场景完全可以替代; 差距就是是否需要对集合过滤;
-F -C
: 会生成变量;COMP_LINE, COMP_POINT, COMP_KEY, COMP_TYPE
;-F
还会生成COMP_WORDS, COMP_CWORD
; 入参:$1
指令名,$2
补全名, 如sta
;$3
不全名前一个单词, 比如git sta
,sta
前一个就是git
; 无过滤规则; 即生成的所有都纳入候选, 需指令提供者或函数提供者根据输入参数进行过滤;
-F
优先: 是shell
函数; 可以通过compgen -A function
查看当前环境下的函数定义; 当然一般是局部自定义的;-F
指定的function
一般结合compgen, comopt
使用; 返回集合通过COMREPLY
数组, 即一般使用COMPREPLY=( hello world )
;
-C
次之: 基本和-F
相同, 除了没有COMP_WORDS, COMP_CWORD
; 输出到标准输出的字符集按\n
进行分词, 生成结果集; 可以结合\
使用;过滤规则:
&
被替代为sta
; 可以用\&
保持愿意;!
则是取反;shopt nocasematch
可以按照不区分大小写匹配;complete -G "*" -X "!&*" todo
; 完全匹配的会被删除; 保留不匹配的;补充
-o
选项;
- 如果前面没有任何匹配集合, 且规则指定了
-o dirnames
; 会将当前目录下的文件夹名纳入匹配, 并过滤; 但是如果有匹配集合这个选项就不会生效;- 指定了
-o plusdirs
; 会将目录纳入集合; 和-o dirnames
区别在于,-o plusdirs
一定会生效;complete -W "hello" -o plusdirs todo
;如果上面的
complete
指定的规则;bash completion,Readline
的补齐规则会被禁用; 可以通过-o bashdefault
开启bash complettion
,-o default
开启Readline completion
作为补充手段, 这个是没有任何生成集合的时候才会生效, 有就不会生效;如果有
compspec
是目录, 会补全/
; 可以通过一些手段规避;complete -W "hello $(find . -maxdepth 1 -mindepth 1 -type d -printf "%f\n")" todo
;compgen -d
生成的也没有后缀;补充: 动态加载规则
- 一般都是一次性的加载; 但是当我们
-F
的返回值是124
的时候, 会重新加载当前指令的complete
; 即返回124
表示当前指令的规则发生了变化;_completion_loader() { . "/etc/bash_completion.d/$1.sh" >/dev/null 2>&1 && return 124 } complete -D -F _completion_loader -o bashdefault -o default
- 默认规则, 用于动态生成某个指令的规则; 即使用了指令并触发才生成;
compgen, complete, compopt
compgen
格式:
compgen [option] [word]
- 这个是手动触发; 即模拟
complete
;option
是可以是complete
的所有选项, 除了-r, -p
;-F -C
在这里也不太好用; 因为相关参数没有设置;word
用于过滤; 即git sta
案例; 可以理解为sta
就是这里的word
参与过滤; 没有就不过滤; 返回所有;- 具体使用参见后面案例
compopt
格式:
compopt [-o option] [-DEI] [+o option] [name]
, 即控制某个指令的选项开关; 不修改其他规则;
-o option
: 打开某个选项;+o option
: 关闭某个选项;name
: 指定具体指令, 未指定表示当前指令;[-DEI]
: 指定是name
忽略;
complete
complete [-abcdefgjksuv] [-o comp-option] [-DEI] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] name [name ...] complete -pr [-DEI] [name ...]
简介
-abcdefgjksuv
是-A action
的简写, 后面介绍;-o comp-option
: 补充手段开关;-DEI
默认规则或空指令规则;-A action
:bash
提供生成集合;-G
自定义文件扩张生成集合;-W "hello world $(ls) $((1+1)) ${var}"
: 按照${IFS}
分词生成自定义集合;-F function
:shell
脚本生成集合;-C
指令生成集合;-X
文件扩张过滤器;-P -S
前后缀;-pr
输出或删除;
complete -pr [-DEI] [name ...]
-p
: 输出name ...
对应的规则; 未指定name
则是输出所有;
-r
: 删除name ...
对应的规则; 未指定name
则是删除所有;
-pr [-DEI]
: 删除默认, 空白指令规则,name
会忽略;
-D
: 默认选项, 即某指令没有规则, 则使用默认规则; 为所有没有规则的指令提供的规则;
-E
: 空规则; 即没有指定指令的时候的规则; 即空白行;
-I
: 部分不支持; 即指令补齐规则;; |
也会触发;
-DEI
同时指定;-D > -E > -I
; 优先级小的会被忽略;
-o comp-option
-o bashdefault
: 有规则默认关闭; 开启后规则未生成任何候选项时才触发bash
补齐规则;
-o default
: 有规则默认关闭; 开启后规则未生成任何候选项时才触发Readline
补齐规则;
-o dirnames
: 开启后规则未生成任何候选项时, 提供当前目录下的文件夹, 会有后缀/
;
-o filenames
: 开启后规则未生成任何候选项时, 提供当前目录下的所有文件; 并会进行文件夹加/
, 特殊字符文件或文件夹括起来; 消除末尾空格; 一般和-F
使用;
-o noquote
: 关闭路径中特殊符号处理; 默认会括起来;
-o nosort
: 结果集不进行排序; 默认排序;
-o nospace
: 补齐后不添加空格; 默认添加空格; 这种一般很少用; 都会添加空格分词;
-o plusdirs
: 规则有匹配集合时才添加;并规律; 且有后缀/
;
-A action
: 都可以通过compgen -A action
查看生成集合; 一般在函数中使用;
-A alias | -a
: 提供alias
集合到候选项;
-A arrayvar
: 提供bash
中array
类型变量名到候选项;
-A binding
:bash Readline key binding names
添加到候选项;
-A builtin | -b
:bash
内置指令添加到候选项;
-A command | -c
: 将指令添加到候选项;
-A directory | -d
: 将当前路径名加入候选项;
-A disabled
: 将shell
禁用的内置指令纳入候选项; 一般为空;
-A enabled
: 将shell
启用的内置指令纳入候选项;
-A export | -e
: 将shell export
的变量名纳入候选项;
-A file | -f
: 将当前文件夹下的所有文件夹和文件纳入候选项;
-A function
: 将当前环境所有可访问的shell
函数纳入候选项;declar -f function_name
查看代码;shopt -s extdebug;declare -F function_name
查看函数定义的文件位置; https://www.cyberciti.biz/faq/how-to-find-bash-shell-function-source-code-on-linuxunix/
-A group | -g
: 用户组名纳入候选项;
-A helptopic
: 内置指令help
支持的命令;
-A hostname
: 查看当前houstname
, 一般是HOSTFILE
指定的文件;
-A job | -j
:job
控制的所有程序; 死的活的;
-A keyword | -k
:bash
内置的关键字, 如if
之类的;
-A running
:job
控制的程序, 活的;
-A service | -s
: 将当前所有服务名纳入候选项;
-A setopt
:set
指令可以通过-o
指定的所有合法选项纳入候选项;
-A signal
: 将当前系统信号名纳入候选项;
-A stopped
:job
控制死的程序;
-A user | -u
: 当前系统所有用户名纳入候选项;
-A variable | -v
: 将当前环境所有shell
变量名纳入候选项;
-C command
: 指令
-F function
-G globpat
-P prefix | -S suffix
: 集合前都添加前缀, 后缀;
-W wordlist
-X filterpat
本文主要讲了什么是规则; 规则怎么选择的; 候选项怎么生成的; 以及指令
compgen
模拟;complete
定义;compopt
动态修改;-F
返回124
重新加载;
https://github.com/scop/bash-completion/
-F -W
生成的路径无/
,-A,-o
开启的选项有;function test_func { # echo "$1:$2:$3:${COMP_WORDS[@]}:" >> log.txt local cur=${COMP_WORDS[1]} local param=$2 case ${COMP_WORDS[1]} in sdk2app) if [[ "${param}" == "" ]] ; then COMPREPLY=($(compgen -W "front back" -- $param)) else COMPREPLY=("sdk2app") fi;; *) COMPREPLY=($( compgen -W "sdk2app" $cur));; esac; } complete -F test_func todo
$ complete -E -W todo $ complete complete -W 'todo' -E $ <tab>
function _comp { echo $1:$2:$3 >> log.txt COMPREPLY=( aaa bbb ) } complete -F _comp test.sh
/aaa/bbb/test.sh的规则不存在, 使用test.sh的规则.
ch@ch:~/ch/shfile/complete$ source func.sh ch@ch:~/ch/shfile/complete$ complete complete -F _comp test.sh ch@ch:~/ch/shfile/complete$ /aaa/bbb/test.sh aaa bbb ch@ch:~/ch/shfile/complete$ /aaa/bbb/test.sh
ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ source func.sh ch@ch:~/ch/shfile/complete$ complete complete -A signal todo ch@ch:~/ch/shfile/complete$ todo SIG SIGABRT SIGCONT SIGINT SIGKILL SIGQUIT SIGRTMAX-11 SIGRTMAX-2 SIGRTMAX-6 SIGRTMIN SIGRTMIN+12 SIGRTMIN+2 SIGRTMIN+6 SIGSEGV SIGTERM SIGTTOU SIGVTALRM SIGALRM SIGFPE SIGIO SIGPIPE SIGRTMAX SIGRTMAX-12 SIGRTMAX-3 SIGRTMAX-7 SIGRTMIN+1 SIGRTMIN+13 SIGRTMIN+3 SIGRTMIN+7 SIGSTKFLT SIGTRAP SIGURG SIGWINCH SIGBUS SIGHUP SIGJUNK(32) SIGPROF SIGRTMAX-1 SIGRTMAX-13 SIGRTMAX-4 SIGRTMAX-8 SIGRTMIN+10 SIGRTMIN+14 SIGRTMIN+4 SIGRTMIN+8 SIGSTOP SIGTSTP SIGUSR1 SIGXCPU SIGCHLD SIGILL SIGJUNK(33) SIGPWR SIGRTMAX-10 SIGRTMAX-14 SIGRTMAX-5 SIGRTMAX-9 SIGRTMIN+11 SIGRTMIN+15 SIGRTMIN+5 SIGRTMIN+9 SIGSYS SIGTTIN SIGUSR2 SIGXFSZ ch@ch:~/ch/shfile/complete$ todo SIGA SIGABRT SIGALRM ch@ch:~/ch/shfile/complete$ todo SIGA
指令如上:
complete -A signal todo
ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ complete ch@ch:~/ch/shfile/complete$ complete -f todo ch@ch:~/ch/shfile/complete$ ls func.sh log.txt test.txt ch@ch:~/ch/shfile/complete$ todo func.sh log.txt test.txt ch@ch:~/ch/shfile/complete$ export FIGNORE=.sh ch@ch:~/ch/shfile/complete$ ls func.sh log.txt test.txt ch@ch:~/ch/shfile/complete$ todo log.txt test.txt ch@ch:~/ch/shfile/complete$ todo
先执行
complete -r
清理所有规则;complete
查看规则;ls
查看文件,todo
补齐显示所有文件; 定义变量FIGNORE
设置过滤文件; 再次todo
补全, 则无.sh
文件;
注意: 支持过滤;ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ complete ch@ch:~/ch/shfile/complete$ complete -G "*.txt" todo ch@ch:~/ch/shfile/complete$ todo log.txt test.txt ch@ch:~/ch/shfile/complete$ todo lo log.txt test.txt ch@ch:~/ch/shfile/complete$ todo lo
根据执行结果可以看到不会过滤匹配
lo
的;这种不建议使用;同样受FIGNORE
影响, 可自行尝试;ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ complete ch@ch:~/ch/shfile/complete$ complete -W "hello world $(echo good) $(for i in {1..9};do echo $i;done;) $((100+100))" todo ch@ch:~/ch/shfile/complete$ todo 1 2 200 3 4 5 6 7 8 9 good hello world ch@ch:~/ch/shfile/complete$ todo 2 2 200 ch@ch:~/ch/shfile/complete$ todo 2
可以看到在
-W
中进行了很多的shell
操作,bash
会进行指令扩张, 然后使用${IFS}
进行分词; 分词结果就是候选项;Readline
会进行排序, 可以通过开关关闭排序;-o nosort
;ch@ch:~/ch/shfile/complete$ complete -o nosort -W "hello world $(echo good) $(for i in {1..9};do echo $i;done;) $((100+100))" todo ch@ch:~/ch/shfile/complete$ todo hello world good 1 2 3 4 5 6 7 8 9 200
function _comp { echo $1:$2:$3:${2:2} >> log.txt COMPREPLY=( $(compgen -W "hello world" -- ${2:2}) ) echo reply:${COMPREPLY[@]} >> log.txt } complete -F _comp -P "--" todo
执行过程
ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ source func.sh ch@ch:~/ch/shfile/complete$ complete complete -P '--' -F _comp todo ch@ch:~/ch/shfile/complete$ todo -- --hello --world ch@ch:~/ch/shfile/complete$ todo --hello
需要注意: 生成的候选列可能会替换修改,
$2
即补全单词; 导致一些小bug; 去掉代码中的${2:2}
就可以看到;
不懂compgen
什么作用的可以直接执行指令compgen -f && ls
, 观察输出结果;ch@ch:~/ch/shfile/complete$ complete -f -X '!&*' todo ch@ch:~/ch/shfile/complete$ todo func.sh log.txt test.txt tt.txt ch@ch:~/ch/shfile/complete$ todo t test.txt tt.txt ch@ch:~/ch/shfile/complete$ todo t
可以看到自行过滤一些;过滤匹配的, 前置
!
表示过滤不匹配的;这里是常规匹配;下面试一下func.sh
不参与匹配;ch@ch:~/ch/shfile/complete$ complete -f -X '!&*' -X "*.sh" todo ch@ch:~/ch/shfile/complete$ todo log.txt test.txt tt.txt ch@ch:~/ch/shfile/complete$ todo
可以看到
sh
结尾的被过滤掉了;
-X '!&*'
为什么是单引号? 因为双引号会进行bash
处理,!
有特殊含义;function _comp { echo $1:$2:$3:${2:2} >> log.txt COMPREPLY=( $(compgen -W "hello world" -- ${2:2}) ) echo reply:${COMPREPLY[@]} >> log.txt } complete -F _comp -P "--" todo
和上面的重复直接拷贝了; 直接看下面的执行结果;
ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ complete ch@ch:~/ch/shfile/complete$ source func.sh ch@ch:~/ch/shfile/complete$ complete complete -P '--' -F _comp todo ch@ch:~/ch/shfile/complete$ todo -- --hello --world ch@ch:~/ch/shfile/complete$ todo --hello
可以看到为所有结果加了前缀
--
; 然后进行匹配;_COMPFLAG=124 function _comp { echo $1:$2:$3:${2:2} >> log.txt COMPREPLY=( $(compgen -W "hello world" -- ${2:2}) ) echo reply:${COMPREPLY[@]} >> log.txt compopt -o plusdirs $1 local temp=${_COMPFLAG} _COMPFLAG=0 echo ${temp} >> log.txt return ${temp} } complete -F _comp -P "--" todo
ch@ch:~/ch/shfile/complete$ complete ch@ch:~/ch/shfile/complete$ complete -r ch@ch:~/ch/shfile/complete$ complete ch@ch:~/ch/shfile/complete$ source func.sh ch@ch:~/ch/shfile/complete$ complete complete -P '--' -F _comp todo ch@ch:~/ch/shfile/complete$ todo --hello temp/ --world ch@ch:~/ch/shfile/complete$ complete complete -o plusdirs -P '--' -F _comp todo
可以看到前后变化; 和官方有点出入;如果不用
_COMPFLAG
机制, 会一直重新加载; 无任何生成;_completion_loader() { . "/etc/bash_completion.d/$1.sh" >/dev/null 2>&1 && return 124 } complete -D -F _completion_loader -o bashdefault -o default