Say I have a command named “foo” that takes one argument (either “encrypt” or “decrypt”) and then a list of files. I want to write a bash completion script that helps with the first argument and then enables standard file completion for the others. The closest I’ve been able to come is this:
complete -F _foo foo
function _foo()
{
local cmds cur
if [ $COMP_CWORD -eq 1 ]
then
cur="${COMP_WORDS[1]}"
cmds=("encrypt decrypt")
COMPREPLY=($(compgen -W "${cmds}" -- ${cur}) )
else
COMPREPLY=($(compgen -f ${COMP_WORDS[${COMP_CWORD}]} ) )
fi
}
This does the first argument correctly and will chose a filename from the current directory for the subsequent ones. But say the current directory contains a subdirectory bar that contains a file baz.txt. After typing ba-TAB-TAB, completion results in “bar ” (space after the “r”) and is ready to choose the next argument. What I want is the standard bash completion, where the result is “bar/” (no space after the slash), ready to choose a file in the subdirectory. Is there any way to get that?
The bash documentation can be a little enigmatic. The simplest solution is to alter the completion function binding:
This means the function returns filenames (including directories), and special handling of the results is enabled.
(IMHO the documentation doesn’t make it clear that this effectively post-processes
COMPREPLY[]as set by your completion function; and that some of the-ooptions, that one included, when applied tocompgenappear to have no effect.)You can get closer to normal bash behaviour by using:
that gets you “~” completion back.
There are two problems with the above however:
$VARIABLEexpansion won’t work,$will become\$to better match a filename with a$. Similarly@hostexpansion won’t work.The only way that I have found to deal with this is to process the
compgenoutput, and not rely on the “filenames” post-processing:(I also removed the superfluous array for
cmdin the above, and madecompgenmore robust and handle spaces or leading dashes in filenames.)The downsides now are that when you get the intermediate completion list (i.e. when you hit tab twice to show multiple matches) you won’t see a trailing / on directories, and since
nospaceis enabled the~ $ @expansions won’t grow a space to cause them to be accepted.In short, I do not believe you can trivially mix-and-match your own completion and the full bash completion behaviour.