15

我有一个命令行工具,它以三部分形式接受参数:

$ t first_second_third

我有一组有效值first,一组有效值second,一组有效值third。我想使用 Bash 完整功能来完成选项值的每个部分,如下例所示:

$ t [tab][tab]           # shows options for first part
walk run skip bike
$ t w[tab]               # completes first part and appends delimiter
$ t walk_[tab][tab]      # shows options for second part
home work park
$ t walk_h[tab]          # completes second part and appends delimiter
$ t walk_home_[tab][tab] # shows options for second part
morning afternoon evening
$ t walk_home_a[tab]     # completes second part and appends space
$ t walk_home_afternoon

我有这个代码:

_tool () {
  local cur="${COMP_WORDS[COMP_CWORD]}"

  local first="walk run skip bike"
  local second="home work park"
  local third="morning afternoon evening"

  case "${cur}" in
    *_*_*)
      COMPREPLY=( $(compgen -W "${third}" -- "") ); return 0;;
    *_*)
      COMPREPLY=( $(compgen -W "${second}" -S "_" -- ${cur}) ); compopt -o nospace; return 0;;
    *)
      COMPREPLY=( $(compgen -W "${first}" -S "_" -- ${cur}) ); compopt -o nospace; return 0;;
  esac
}

complete -F _tool t

第一个子句效果很好;那里没什么特别的。但是第二个子句没有给我任何选择或完成。

$ t [tab][tab]
bike_  run_   skip_  walk_
$ t b[tab]
$ t bike_[tab][tab] # nothing but bells

我将第二个子句替换为以下内容(即,我${cur}在调用中替换为空字符串COMPREPLY):

COMPREPLY=( $(compgen -W "${second}" -S "_" -- "") ); compopt -o nospace; return 0;;

我在命令行上获得了所有选项的列表,但没有完成。

$ t bike_[tab][tab]
home_  park_  work_
$ t bike_h[tab][tab]
home_  park_  work_
$ t bike_ho[tab][tab]
home_  park_  work_

我认为可能与单词列表有一些不好的交互${cur}COMPREPLY所以我再次更改了第二个子句,添加了一个与当前单词的第一部分匹配的前缀:

local prefix=( $(expr "${cur}" : '\(.*_\)') )
COMPREPLY=( $(compgen -W "${second}" -P "${prefix}" -S "_" -- "") ); compopt -o nospace; return 0;;

这没有多大帮助。${cur}仍然在命令COMPREPLY中,我再次没有选项或完成。相反,使用空字符串,我得到了完整的选项(不仅仅是当前部分)。但是,按 Tab 会删除键入的内容。

$ t bike_[tab][tab]
bike_home_  bike_park_  bike_work_
$ t bike_ho[tab]
$ t bike_ # "ho" was erased

当然,我对第三部分也有同样的问题。

  1. 如何让 Bash 部分地完成当前单词?
  2. 在选择一个选项之前,如何让 Bash 不附加分隔符?

这类似于另一个 Bash 完整问题,但我不想列出所有可能的值排列,并且它们没有格式化为文件名(不确定文件名模式是否有所不同)。

谢谢您的帮助!
--ap

4

1 回答 1

5

我能够创造出你想要的东西。请参阅下面的代码:

_tool () {
  local cur="${COMP_WORDS[COMP_CWORD]}"

  local first="walk run skip bike"

  case "${cur}" in
    *_*_*)
      local firstsecond=`echo ${cur} | awk -F '_' '{print $1"_"$2}'`
      local third="${firstsecond}_morning ${firstsecond}_afternoon ${firstsecond}_evening"
      COMPREPLY=( $(compgen -W "${third}" -- ${cur}) ); return 0;;
    *_*)
      local firstcomp=`echo ${cur} | awk -F '_' '{print $1}'`
      local second="${firstcomp}_home ${firstcomp}_work ${firstcomp}_park"
      COMPREPLY=( $(compgen -W "${second}" -S "_" -- ${cur}) ); compopt -o nospace; return 0;;
    *)
      COMPREPLY=( $(compgen -W "${first}" -S "_" -- ${cur}) ); compopt -o nospace; return 0;;
  esac
}

complete -F _tool t

我对自动完成不太熟悉,这是我第一次使用compgencomplete,所以我不确定我能回答你关于事物理论的问题。我可以告诉你我做了什么,以防它帮助你理解这个记录不充分的函数是如何工作的。首先,我试图了解你所拥有的。我成功地重现了你的问题。然后我有了一个想法,即使你不想自己在代码中列出每个排列,我也可以使用局部变量来模仿它。我做的第一件事是在我们有了第一个下划线之后获取“第一个”元素,并将第二个元素添加到它,并将其提供给 compgen。令我惊讶的是,它运行良好。然后我尝试对第三级做同样的事情......然后问题开始了。我发现第二部分并没有我想象的那么好。完成第一个单词后,我会得到第二个单词的建议;

$ t [tab][tab]           # shows options for first part
bike_ run_ skip_ walk_
$ t w[tab]               # completes first part and appends delimiter
$ t walk_[tab][tab]      # shows options for second part
walk_home_ walk_park_ walk_work_
$ t walk_h[tab]          # completes second part and appends delimiter
$ t walk_home_[tab][tab] # screws up with it
$ t walkhome_            

我尝试将下划线移动到 awk 中,但没有成功:

      local first=`echo ${cur} | awk -F '_' '{print $1"_"}'`
      local second="${first}home ${first}work ${first}park"

出于某种原因,我不得不将变量名称更改为不同于“first”的名称,它开始工作。然后,在第三个元素上,我遇到了与您上次描述的相同的问题,我在第三个元素中键入的任何内容都会消失,但 [tab][tab] 会给我选项:

$ t walk_home_[tab][tab] # shows options for second part
morning afternoon evening
$ t walk_home_aft[tab]     # erases what I typed
$ t walk_home_

我在第三个单词中添加${cur}到 compgen 并且它起作用了。这只是我所做工作的总结......但我有一个工作版本,所以我很满意。:-)

于 2013-05-04T02:45:41.357 回答