Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I would like to create completion rule for comma-separated parameters list. E.g. I have command which receives list of server names:

myscript -s name1,name2,name3

At this moment I've managed to write following completion:

_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
}

But it has at least 2 problems:

  1. Suggestions for current value includes all previous values in their prefix.
  2. It doesn't consider duplicate values.

What is the best practices for such cases? Maybe bash-completions has some bundled functions for csv-lists?

share|improve this question
3  
What might help is that you can split comma separated values into an iterable list like this: IFS=, LIST=("$VARIABLE") where $VARIABLE contains your comma separated values. –  Michael Ehrenreich Apr 21 at 14:56
1  
Nice idea @MichaelEhrenreich, but you mustn't quote the $VARIABLE, otherwise word breaking doesn't happen. just use IFS=, LIST=($VARIABLE). –  Guss Jun 5 at 6:06

1 Answer 1

There's basically no way to fix the problems you describe, because bash uses the values in COMPREPLY directly in the display and then to replace the user's text - while to get what you want, you'd need to first generate the possible completions (just the additional server names, without the prefix) for bash to display, then when bash is about to replace the user text with the longest non-conflicting string, you'd need it to call your script again to generate the text with the prefix - and bash has no facility for that.

The best that I could come up with is to have the COMPREPLY be generated with only the first word having the entire prefix (COMPREPLY=( "${prefix},"$(compgen -W "${servers[@]}" -- ${realcur}) )), so that if there is only one possible completion, it completes automatically correctly, while if there is more than one possible completion, then bash will not remove what was typed so far (because the first word in COMPREPLY has the entire prefix and thus matches the currently typed text and will be selected by bash to replace the user's text) and will display the options without the prefix - except for that one word that already contains the prefix, so the output will look like this:

$ command -s banana,a
ananas     apricot    banana,apple

"apple" as sorted last in the completion options because it carries the prefix which starts with "b" - very confusing. So I don't recommend doing that.

Regarding the duplicates - in order to not show duplicates, you just need to break $prefix into its part (easy: IFS="," prefix_parts=($prefix)) and then iterate over them and only leave in $servers the names that are not already listed. Its tedious to type, so I won't show it here, but relatively trivial so I'm sure you can manage :-).

To summarize, I don't think you should use comma separated values for input options, at least if you expect bash to help you with completion.

You can support an options format that like this: command -s <server> [<server> [..]] and then for completion of entries other than the one immediately after the -s option, just scan back through the $COMP_WORDS array from $COMP_CWORD until you find an option (string that matches -*) and if its "-s" then you need to do the server name completion.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.