Bash – getting case insensitive completions with compgen in bash

autocompletebash

I'm trying to get my command (devmode2) auto-completing with bash working with compgen -W, my bashrc has:

_devmode2() {
    COMPREPLY=()
    local sonames=$(devmode2 --auto --current "${COMP_CWORD}" -- ${COMP_WORDS[@]})
    COMPREPLY=($(compgen -W "${sonames}" -- ${COMP_WORDS[COMP_CWORD]}))
}
complete -F _devmode2 devmode2

An example auto-completion

$ devmode2 g<tab>

results in sonames having the following values:

game-life-adv
game-life-sparse
getopt-alt
git-workflow
git-workflow-extra
github-testers
group-git
group-git-ivan
Group-Git-Taggers-Maven
Group-Git-Taggers-Node
Group-Git-Taggers-Perl
Group-Git-Taggers-Ruby

but my auto-completion results in the following suggestions:

game-life-adv       game-life-sparse    getopt-alt          github-testers      git-workflow        git-workflow-extra  group-git           group-git-ivan

How can I get all the values I pass to compgen back?

Best Answer

As far as I know, compgen does not have an option to do case-insensitive filtering of the list of options you give it.

There is a readline variable which you can set to cause filename completion to be case-insensitive:

bind "set completion-ignore-case on"

but that doesn't affect the behaviour of compgen, so it probably won't apply to programmable completion. (Its behaviour is a bit odd with filenames, too.)

That leaves you with the possibility of doing your own filtering instead of using compgen (or in conjunction with compgen if you require features other than filtering, which is not the case in your example).

You could do that with a simple iteration over the list of alternatives, doing a case-insensitive prefix comparison, but the following little function should work:

_devmode2() {
  local sonames=($(devmode2 --auto --current "${COMP_CWORD}" -- ${COMP_WORDS[@]}))
  local prefix="${COMP_WORDS[COMP_CWORD]}"
  COMPREPLY=($(printf %s\\n "${sonames[@]}" |
               awk -v IGNORECASE=1 -v p="$prefix" \
                   'p==substr($0,0,length(p))'))
}

If devmode2 outputs one alternative per line, it would be easier to just use it as the input to awk. Also, the second argument of the completion function is, as far as I know, always the same as ${COMP_WORDS[COMP_CWORD]}. So this might be simpler:

_devmode2() {
  COMPREPLY=($(devmode2 -auto --current "${COMP_CWORD}" -- "${COMP_WORDS[@]}" \
               awk -v IGNORECASE=1 -v p="$2" \
                   'p==substr($0,0,length(p))'))
}
Related Question