逗号分隔值的 Bash 补全

anl*_*lar 17 bash autocomplete

我想为逗号分隔的参数列表创建完成规则。例如,我有接收服务器名称列表的命令:

myscript -s name1,name2,name3
Run Code Online (Sandbox Code Playgroud)

此时我已经设法编写了以下完成:

_myscript () {
  local cur prev opts

  _get_comp_words_by_ref cur prev

  opts='-s'

  servers='name1 name2 name3'

  if [[ ${cur} == -* ]] ; then
    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
  else
    case "${prev}" in
      -s)
        if [[ "$cur" == *,* ]]; then
          local realcur prefix
          realcur=${cur##*,}
          prefix=${cur%,*}
          COMPREPLY=( $(compgen -W "${servers}" -P "${prefix}," -- ${realcur}) )
        else
          COMPREPLY=( $(compgen -W "${servers}" -- ${cur}) )
        fi
        ;;
      *)
        # do nothing
        ;;
    esac
  fi
}
Run Code Online (Sandbox Code Playgroud)

但它至少有两个问题:

  1. 当前值的建议包括其前缀中的所有先前值。
  2. 它不考虑重复值。

这种情况下的最佳做法是什么?也许 bash-completions 有一些 csv-lists 的捆绑功能?

Gus*_*uss 6

基本上没有办法解决你描述的问题,因为 bashCOMPREPLY直接在显示中使用值,然后替换用户的文本 - 而要得到你想要的,你需要首先生成可能的完成(只是额外的服务器名称,不带前缀)以便 bash 显示,然后当 bash 即将用最长的非冲突字符串替换用户文本时,您需要它再次调用您的脚本以生成带有前缀的文本 - 和 bash没有设施。

我能想到的最好的方法COMPREPLY是生成只有第一个单词具有整个前缀 ( COMPREPLY=( "${prefix},"$(compgen -W "${servers[@]}" -- ${realcur}) )),这样如果只有一个可能的完成,它会自动正确完成,而如果有多个可能的完成,那么 bash 将不会删除到目前为止输入的内容(因为 in 的第一个单词COMPREPLY具有完整的前缀,因此与当前输入的文本匹配,并且将被 bash 选中以替换用户的文本)并且将显示不带前缀的选项 - 除了对于已经包含前缀的单词,因此输出将如下所示:

$ command -s banana,a
ananas     apricot    banana,apple
Run Code Online (Sandbox Code Playgroud)

“apple”在完成选项中排在最后,因为它带有以“b”开头的前缀 - 非常混乱。所以我不建议这样做。

关于重复项 - 为了不显示重复项,您只需要打破$prefix它的部分(简单IFS="," prefix_parts=($prefix):)然后迭代它们并且只留下$servers尚未列出的名称。打字很乏味,所以我不会在这里展示它,但相对来说比较琐碎,所以我相信你可以做到:-)。

总而言之,我认为您不应该对输入选项使用逗号分隔值,至少如果您希望 bash 帮助您完成。

您可以支持这样的选项格式:command -s <server> [<server> [..]]然后为了完成-s选项之后的条目以外的条目,只需扫描$COMP_WORDS数组,$COMP_CWORD直到找到一个选项(匹配的字符串-*),如果它是“-s”,则您需要完成服务器名称完成。