Как Bash Завершить шаблон из трех частей

У меня есть инструмент командной строки, который принимает аргументы в трехчастной форме:

$ 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, я не получил никаких параметров или дополнений еще раз. Вместо этого вместо пустой строки я получил полную опцию (а не только текущую часть). Но нажатие вкладки стирает то, что набирается.

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

Конечно, у меня такая же проблема и с третьей частью.

  • Как я могу получить Bash для завершения текущего слова, частично по частям?
  • Как я могу получить Bash, чтобы не добавлять разделитель до тех пор, пока не будет выбран параметр?

Это похоже на еще один вопрос Bash, но я не хочу перечислять все возможные перестановки значений, и они не отформатированы как имена файлов (не конечно, если шаблон имени файла имеет значение).

Спасибо за помощь! --ap

Ответ 1

Я смог создать то, что вы хотите. Смотрите мой код ниже:

_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

Я не очень хорошо знаком с автозаполнением, и это был первый раз, когда я использовал compgen или complete, поэтому я не уверен, что могу ответить на ваши вопросы о теории вещи. Я могу рассказать вам, что я сделал, если это поможет вам понять, как работают эти плохо документированные функции. Сначала я попытался понять, что у вас было. Я успешно воспроизвел ваши проблемы. Тогда у меня возникла идея, что, даже если вы не хотите перечислять каждую перестановку самостоятельно в коде, я мог бы использовать локальные переменные для имитации, как если бы это было. Первое, что я сделал, это получить "первый" элемент после того, как мы получили первый знак подчеркивания, и добавим второй элемент к нему, и напишем это для компиляции. К моему удивлению, все получилось отлично. Затем я попытался сделать то же самое на 3-м уровне... и затем начались проблемы. Я узнал, что вторая часть работает не так хорошо, как я себе представлял. после завершения первого слова, я бы получил предложения для второго; после того, как одно слово будет вторым и нажмите [TAB], первый символ подчеркивания исчезнет.

$ 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"

По какой-то причине мне пришлось изменить имя переменной на нечто отличное от "первого", и оно начало работать. Затем на 3-м элементе у меня была та же самая проблема, о которой вы описали в прошлом, где бы я ни печатал в 3-м элементе, исчезла бы, но [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} для компиляции в третьем слове, и он сработал. Это всего лишь резюме того, что я сделал... но у меня есть рабочая версия, поэтому я доволен ею.: -)