Bash 是否可以为“菜单完成”定义完成规范?

Bash 是否可以为“菜单完成”定义完成规范?,bash,bash-completion,Bash,Bash Completion,我有一个文件夹~/builds,其中包含以下子目录: > tree ~/builds ~/builds ├── projectA │   ├── build_2020_04_10_ok │ ├── build_2020_04_11_ko │ ├── build_2020_04_12_ok │   └── ... └── projectB ├── build_2020_04_10_ok ├── build_2020_04_11_ok ├── build_202

我有一个文件夹
~/builds
,其中包含以下子目录:

> tree ~/builds
~/builds
├── projectA
│   ├── build_2020_04_10_ok
│   ├── build_2020_04_11_ko
│   ├── build_2020_04_12_ok
│   └── ...
└── projectB
    ├── build_2020_04_10_ok
    ├── build_2020_04_11_ok
    ├── build_2020_04_12_ok
    └── ...
当前,当我编写一个以子文件夹之一为参数的命令并使用自动完成时,
bash
列出所有候选项:

> mycmd ~/builds/projectA/[TAB]
> mycmd ~/builds/projectA/build_2020_04_1[TAB][TAB]
build_2020_04_10_ok build_2020_04_11_ko build_2020_04_12_ok ...
这不是我想要的行为

我想要的是像Windows一样的自动完成。我知道我可以通过这样修改
.bashrc
来使用它:

bind '"\C-g": menu-complete'
下面是我按下Ctrl+g键时发生的情况:

> mycmd ~/builds/projectA/[Ctrl+g]
> mycmd ~/builds/projectA/build_2020_04_10_ok[Ctrl+g]
> mycmd ~/builds/projectA/build_2020_04_11_ko[Ctrl+g]
> mycmd ~/builds/projectA/build_2020_04_12_ok
这几乎就是我想要的。我想更改子文件夹的显示顺序(首先是最新的,然后是较旧的)。另外,我想放弃所有的
ko
构建。换句话说,我需要此命令定义的顺序:

ls -dr build*ok
build_2020_04_12_ok/  build_2020_04_10_ok/
因此,我想为
menu complete
定义一个完成规范,以便只在
~/builds
的子目录上执行我想要的行为。我看到可以为执行此操作,但在
菜单“完成”
上找不到执行此操作的信息


有可能吗?

正如@chepner所说,
菜单完成
委托完成与
完成
相同的完成(请参阅)。因此,最初我认为不可能根据是通过
complete
还是
menu complete
来配置不同的完成方式。但我检查了是否根据调用的钩子设置了不同的值,并发现了一些突出的问题:

# Get the current set of environment variables
# the sed expression strips functions to just print variables
# There's probably a better way to do this...
bash-5.0$ get_env() {
  set | sed -n '/^[^=]*$/q;p' | sort
}

# Define a function to use as a completion that simply prints the changes
# in the environment between the normal shell and from within a completion
bash-5.0$ diff_env() {
  echo # extra echo's for readability
  sdiff -s /tmp/env.txt <(get_env)
}
# Register it as a completion (the command doesn't actually need to exist)
bash-5.0$ complete -F diff_env foo

bash-5.0$ bind '"\C-g": menu-complete'

# Persist the initial environment
bash-5.0$ get_env > /tmp/env.txt

bash-5.0$ foo [TAB]
BASH_LINENO=([0]="12")            |    BASH_LINENO=([0]="1" [1]="13")
BASH_SOURCE=([0]="main")          |    BASH_SOURCE=([0]="main" [1]="main")
                                  >    COMP_CWORD=1
                                  >    COMP_KEY=9
                                  >    COMP_LINE='foo '
                                  >    COMP_POINT=4
                                  >    COMP_TYPE=9
                                  >    COMP_WORDS=([0]="foo" [1]="")
                                  >    _=echo
FUNCNAME=([0]="get_env")          |    FUNCNAME=([0]="get_env" [1]="diff_env")
_=get_env                         <

^C
bash-5.0$ foo [Ctrl+g]
BASH_LINENO=([0]="12")            |    BASH_LINENO=([0]="1" [1]="14")
BASH_SOURCE=([0]="main")          |    BASH_SOURCE=([0]="main" [1]="main")
                                  >    COMP_CWORD=1
                                  >    COMP_KEY=9
                                  >    COMP_LINE='foo '
                                  >    COMP_POINT=4
                                  >    COMP_TYPE=37
                                  >    COMP_WORDS=([0]="foo" [1]="")
                                  >    _=echo
FUNCNAME=([0]="get_env")          |    FUNCNAME=([0]="get_env" [1]="diff_env")
_=get_env                         <
^C

希望这就足够了:)如果您需要帮助以您描述的行为实际编写
complete
函数,我建议从当前实现开始另问一个问题(使用
complete-p[command]
查看当前完成情况,然后
键入[function name]
要查看完成的实现情况),应该直接从那里调整现有的行为。

正如@chepner所说,
菜单完成
将委托给与
完成
相同的完成操作(请参阅)。因此,最初我认为不可能根据是通过
complete
还是
menu complete
来配置不同的完成方式。但我检查了是否根据调用的钩子设置了不同的值,并发现了一些突出的问题:

# Get the current set of environment variables
# the sed expression strips functions to just print variables
# There's probably a better way to do this...
bash-5.0$ get_env() {
  set | sed -n '/^[^=]*$/q;p' | sort
}

# Define a function to use as a completion that simply prints the changes
# in the environment between the normal shell and from within a completion
bash-5.0$ diff_env() {
  echo # extra echo's for readability
  sdiff -s /tmp/env.txt <(get_env)
}
# Register it as a completion (the command doesn't actually need to exist)
bash-5.0$ complete -F diff_env foo

bash-5.0$ bind '"\C-g": menu-complete'

# Persist the initial environment
bash-5.0$ get_env > /tmp/env.txt

bash-5.0$ foo [TAB]
BASH_LINENO=([0]="12")            |    BASH_LINENO=([0]="1" [1]="13")
BASH_SOURCE=([0]="main")          |    BASH_SOURCE=([0]="main" [1]="main")
                                  >    COMP_CWORD=1
                                  >    COMP_KEY=9
                                  >    COMP_LINE='foo '
                                  >    COMP_POINT=4
                                  >    COMP_TYPE=9
                                  >    COMP_WORDS=([0]="foo" [1]="")
                                  >    _=echo
FUNCNAME=([0]="get_env")          |    FUNCNAME=([0]="get_env" [1]="diff_env")
_=get_env                         <

^C
bash-5.0$ foo [Ctrl+g]
BASH_LINENO=([0]="12")            |    BASH_LINENO=([0]="1" [1]="14")
BASH_SOURCE=([0]="main")          |    BASH_SOURCE=([0]="main" [1]="main")
                                  >    COMP_CWORD=1
                                  >    COMP_KEY=9
                                  >    COMP_LINE='foo '
                                  >    COMP_POINT=4
                                  >    COMP_TYPE=37
                                  >    COMP_WORDS=([0]="foo" [1]="")
                                  >    _=echo
FUNCNAME=([0]="get_env")          |    FUNCNAME=([0]="get_env" [1]="diff_env")
_=get_env                         <
^C

希望这就足够了:)如果您需要帮助以您描述的行为实际编写
complete
函数,我建议从当前实现开始另问一个问题(使用
complete-p[command]
查看当前完成情况,然后
键入[function name]
要查看完成的实现),应该可以直接从那里调整现有的行为。

complete
menu complete
只是两个Readline函数,它们以不同的方式显示可编程完成的结果。这并不是您想要的,但是如果你以前没见过,请看一看。它提供了一个更强大、更灵活的功能。
complete
menu complete
我认为只是两个以不同方式显示可编程完成结果的Readline函数。这并不是您想要的功能,但如果您以前没有看到过,请看一看。它提供了一个更强大、更灵活的解决方案。