设为首页 加入收藏

TOP

在命令行按下tab键之后, 发生了什么?(一)
2023-07-23 13:25:52 】 浏览:44
Tags:tab


1. 引言

当我们输入ls 再按下TAB时, 会自动列出当前路径下所有的文件;

当我们输入ls a再按下TAB时, 会自动列出当前路径下所有以a开头的文件; 若只有一个以a开头的文件, 将会自动补全;

当我们输入type 再按下TAB时, 会自动列出全所有可执行的命令;

当我们输入docker rmi 再按下TAB时, 会自动列出所有镜像名;

一个显示文件, 一个显示命令, 一个显示容器名, 这是怎么做到的?

本文将带你一探究竟, 并以docker为例, 实现一个简单的docker自动补全规则

2. complete命令

上述功能, 是 Bash 2.05 版本新增的功能, 叫做自动补全. 自动补全允许我们对命令和选项设置补全规则, 按下TAB之后, 会根据我们设置的规则返回补全列表, 当补全列表只有一个元素时, 就会自动补全.

bash自动补全用到最主要的命令就是complete, 这是一个Bash的内置命令(builtin), 用于指定某个命令的补全规则, complete语法如下:

complete [-abcdefgjksuv] [-o comp-option] [-DEI] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name …]
complete -pr [-DEI] [name …]

选项:
    -o comp-option
        定义一些补全的行为, 可以使用的行为如下:
        nospace    补全后不在最后添加空格
        nosort     对于补全列表不要按字母排序

    -A action
        使用预设的补全规则, 可使用的补全规则如下:
        alias        补全列表设置为所有已定义的别名. 等同于-a
        builtin      补全列表设置为所有shell内置命令. 等同于-b
        command      补全列表设置为所有可执行命令. 等同于-c
        directory    补全列表设置为当前路径下所有目录. 等同于-d,
                     也就是说 complete -d xxx 与 complete -A directory xxx 等价, 只是写法不一样
        export       补全列表设置为所有环境变量名. 等同于-e
        file         补全列表设置为当前路径下所有文件. 等同于-f
        function     补全列表设置为所有函数名
        signal       补全列表设置为所有信号名
        user         补全列表设置为所有用户名. 等同于-u
        variable     补全列表设置为所有变量名. 等同于-v

    -F function
        用函数来定义补全规则, 函数运行后 COMPREPLY 变量做为补全列表

    -W wordlist
        用一个字符串来做为补全列表

    -p name
        显示某个命令的补全规则, 如果 name 为空的话则显示所有命令的补全规则

    -r
        移除某个命令的补全规则

ls命令默认的补全列表是当前路径下所有文件, 现在, 我们改变其补全规则, 让其补全列表变为所有可执行命令

$ cd /

# 先测试下 ls 默认的补全规则
$ ls<TAB>
bin/    boot/   dev/    etc/    home/   lib/    lib32/  lib64/  libx32/ media/  mnt/    opt/    proc/   root/   run/    sbin/   srv/    sys/    tmp/    usr/    var/

# 修改 ls 的补全规则, 让所有可执行命令作为其补全列表
$ complete -c ls

# 测试修改补全规则后的 ls
$ ls who<TAB>
who                   whoami                whoopsie              whoopsie-preferences

提示: 上述改变的补全规则只在当前shell有效, 即不会影响到其他用户, 重新登录后也会失效. 所以想要恢复ls命令的补全规则的话, 只需要退出再重新登录服务器就好了. 至于如何永久改变补全规则, 请看后文.


我们再来看下type命令预设的补全规则, 发现type命令设置的补全列表是所有可执行命令

$ complete -p type
complete -c type

至此, 我们应该知道引言中所提出的问题, 为什么ls命令会文件而type命令会列出命令

3. 自定义补全列表

尽管Bash预设了很多补全规则, 但是很明显, 如果我们自己想给docker命令写补全规则的话, 预设的补全规则显然是不能满足我们需求的. 所以, 我们可以用-W选项来自定义补全列表.

假设我们自己写了个mydocker命令, 可以使用的功能有mydocker rm, mydocker rmi, mydocker stop, mydocker start, 显然, mydocker的补全列表为rm rmi stop start, 我们可以使用下面的命令来设置补全规则

# 将 rm rmi stop start 设置为 mydocker 的补全列表
$ complete -W 'rm rmi stop start' mydocker

$ mydocker <TAB>
rm     rmi    start  stop

$ mydocker st<TAB>
start  stop

注意: mydocker命令本身没有自动补全, 需要手动完整输入


到这一步, 我们已经能给相当一部分的命令来定义补全规则了. 但是, 上述的'-W'选项, 是静态的补全规则, 不会随着某些条件的改变而变化; docker rmi <TAB>所有显示的镜像名, 会随着镜像的增删而改变; docker rm <TAB>所有显示的容器名, 会随着容器的增删而改变; 是动态的补全规则, 这是如何做到的呢?

我们直接通过-p选项来查看docker预设的补全规则就好了, 发现docker命令是通过-F _docker来指定补全规则; 再通过type _docker来查看_docker是什么玩意, 发现_docker是一个非常复杂的函数

$ complete -p docker
complete -F _docker docker

$ type _docker
_docker is a function
_docker ()
{
    ......
}

接下来, 我们来好好聊一聊-F这个选项

4. 动态补全列表

-F选项会指定一个函数做为补全规则, 每次按下TAB时, 就会调用这个函数, 并且将COMPREPLY的值做为补全列表, 所以我们需要在函数中处理COMPREPLY变量

除了COMPREPLY变量外, Bash还提供了一些变量来方便我们获取当前的输入

变量名 类型 说明
COMP_LINE 字符串 当前的命令行输入的所有内容
COMP_WORDS 数组 当前的命令行输入的所有内容, 和COMP_LINE不同的是, 这个变量是一个数组
COMP_CWORD 整数 当前的命令行输入的内容位于COMP_WORDS数组中的索引
COMPREPLY 数组 补全列表

接下来我们编写一个补全脚本来测试这些变量, 脚本名字可以随便取, 暂且叫做 test.sh, 文件内容如下:

_complete_test() {
    echo
    echo "COMP_LINE: $COMP_LINE"                # 当前的命令行输入的所有内容(字符串)
    echo "COMP_WORDS: ${COMP_WO
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Linux从文件中逐行读取文件名并将.. 下一篇Zabbix Timeout 设置不当导致的问..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目