Bash completions -C vs -F

autocompletebash

In the bash completion setup (https://github.com/scop/bash-completion) supplied by my system there seems to be an unequivocal preferences towards completing with complete -F as opposed to complete -C.

complete -p | grep -- -C # no match

The difference between the two appears to be that
the -Function-taking form expects a function that reads input from variables and outputs to variables wheres the -Command-taking form reads input from positional arguments and outputs to stdout.

I like the second approach better as it's more functional and the completions functions/programs are slightly easier to debug (as they don't depend on a particular context of variables).

However, https://github.com/scop/bash-completion's preference towards the former makes me wonder: are there any downsides to the -C approach apart from the obvious (negligible for a UI, IMHO) performance overhead of a new process?

Best Answer

I'm not sure why you'd consider complete -C {command} "more functional" than complete -F {function}, since the latter literally takes a function; so the motivation is not clear for your stated preference. But you're right, almost all examples and documentation show a preference for complete ... -F {function} ..., e.g., complete -F _foobar foobar, where the function _foobar is used to generate completion options for a hypothetical command foobar.

Main reasons are, presumably,

  • the function "_foobar" can be used to complete multiple commands, e.g., perhaps a foobar package also has a foo and a bar command; $1 is passed to the function so you know the name of the command being completed.
  • the function variant seems to provide a lot more information to perform completions; the "-C" variant seems dumber by comparison (but, admittedly, I've always used the "-F" variant myself.)
  • it's easier to set the completion options via the function (via the COMPREPLY array variable), than to rely on a command's output; the command runs in a subprocess so it's harder to pass info into it, and get info out of it.
  • the "-C command" variant spawns a subprocess, which, when pressing "tab-tab-tab" for completions, creates additional delays, when you really want this to feel instantaneous.
  • the "-C command" variant would either have a long & complicated command in the complete statement, or it'd call an external shell script, in which case you'd have to ship and keep track of the location of that script (a pain).

Also, in my personal "completions", I have to get them working across older versions of bash and for various Unix's (AIX, HP-UX, Solaris, Linux, BSD), and I've found a lot of variety in what is supported. Since "complete" returns true when it works, I use the following idiom which seems to be more portable across bash versions (I only included this here, since it shows the usage of "-F {function} {commmand}" is simpler/cleaner than a corresponding "-C "command"" would be):

complete -o bashdefault -o default -o nospace -F _cmd cmd 2>/dev/null \
|| complete             -o default -o nospace -F _cmd cmd 2>/dev/null \
|| complete             -o default            -F _cmd cmd 2>/dev/null \
|| complete                                   -F _cmd cmd

Likewise, if I have cmd1, cmd2, cmd3 that all take basically the same completions (e.g., consider a completion script for scp and ssh that take a list of hostnames which you generate),

for c in cmd1 cmd2 cmd3; do
    # or that long complete statement shown above
    complete -o bashdefault -o default -o nospace -F _cmd $c 2>/dev/null
done

For reference:

Related Question