I don't like any of the answers here and would like to contribute a couple concrete examples.
I sometimes have a number of directories with a common prefix and wanted to be able to cd into them with completion by just typing the unique part of the directory name which would be in the middle or end of the directory name.
I was able to write a completion function to accomplish this:
_cd(){
# Backup old nullglob setting
local shoptbakup="`shopt -p nullglob`"
shopt -s nullglob
local cur opts i opt
local IFS=$'\n'
cur="${COMP_WORDS[COMP_CWORD]}"
# Home directory substitution
if [[ "${cur:0:1}" == '~' ]]; then
cur="$HOME/${cur:1}"
fi
if [[ $cur == */* ]]; then
opts=(${cur}*/)
else
opts=(*${cur}*/)
fi
# Prevent trailing//
i=0
for opt in "${opts[@]}"; do
#opts[$i]=${opt%${opt##*[!/]}}
opts[$i]=${opt%/}
i=$((i+1))
done
# Restore nullglob setting
eval "$shoptbakup" 2>/dev/null
COMPREPLY=("${opts[@]}")
}
complete -o filenames -o bashdefault -o nospace -F _cd cd
So now from my prompt I can do this:
$ ls
test 6/ test 7/ test 8/ test1/ test2/ test3/ test4/ test5/
$ cd 7[TAB]
And it automatically completes to:
$ cd test\ 7/
Another reason you might want to use programmable completion would be if you write script and want to be able to either list or complete options for you. This page has some really good example:
http://fahdshariff.blogspot.com/2011/04/writing-your-own-bash-completion.html
Here's an example:
Let's say I have a script called listanimals. I can write a completion function to assist with filling in the options
# complete sendevent
_listanimals()
{
local cur=${COMP_WORDS[COMP_CWORD]}
local prev=${COMP_WORDS[COMP_CWORD-1]}
if [[ $prev == "--sort-direction" ]]; then
COMPREPLY=( $( compgen -W "horizontal vertical" -- $cur ) )
return 0
elif [[ $prev == "--type" ]];then
COMPREPLY=( $( compgen -W "mammals reptiles birds fish marsupials insects misc" -- $cur ) )
return 0
elif [[ $prev == "--size" ]]; then
COMPREPLY=( $( compgen -W "tiny small medium large huge" -- $cur ) )
return 0
fi
# completing an option
if [[ "$cur" == --* ]] || [[ $cur == "" ]]; then
COMPREPLY=( $( compgen -W "--sort-direction --type --size" -- $cur ) )
fi
}
complete -F _listanimals listanimals
Now I can do this:
$ listanimals --[TAB][TAB]
--size --sort-direction --type
$ listanimals --t[TAB]
Which will automatically fill in this:
$ listanimals --type
Then I can do this:
$ listanimals --type [TAB][TAB]
birds fish insects mammals marsupials misc reptiles
And I'm sure you can figure out the rest. This is a working example so if you want to try it out just copy/paste it into a file (e.g. sample.sh) and source it in bash source sample.sh
. You don't actually need to have a script named listanimals.
Hopefully someone will find this useful because I found it a pain to figure out.