When creating shell completion functionality in bash, it seems like the -C
option would be powerful and common. It's defined like this:
-C command
command is executed in a subshell environment, and its
output is used as the possible completions.
That seems really straightforward. But, weirdly, it's not. If I do:
$ complete -C "echo one two three" test.sh
and then type test.sh
and hit [tab] one time, I get that line filled out with
$ test.sh one two three test.sh test.sh
back. What?! For comparison,
$ complete -W "one two three" test.sh
does exactly what I'd expect: it offers one, three, or two as possible completions (in sort order, of course).
I can't find any good documentation anywhere, so I'm experimenting…. maybe a semicolon helps? complete -C "echo one two three;" test.sh
… Ah, okay, this helps some, because it removes that weird test.sh
from the end. But it still fills out the full string one two three
. This is odd, because that description above definitely says "possible completions", plural.
… so, hmmm, ah, newlines? Let's try complete -C "echo -e 'one\ntwo\nthree';" test.sh
.
Okay, so, this is promising. If I type test.sh [tab]
, I get back one three two
. But then, things go wrong. I add an o
to resolve the ambiguity, so, test.sh o[tab]
, and that returns
o one three two
Which is puzzling. In fact, any string I type gets added to the list; if I do test.sh wtf[tab]
, I get one three two wtf
echoed back. I guess possibly I need to do something sophisticated with compgen
, but, again, I stress, this works as expected with -W
. And, in fact
$ complete -W '$(echo one two three)' test.sh
works just fine (note single quotes to prevent premature expansion/execution, in case you do something more sophisticated than echo
which could produce variable results).
What am I missing here?
Best Answer
It's the job of the
-C
command to figure out what text to insert in response to a completion request. The interface is a bit strange:The argument to
-C
is interpreted as a bash script snippet. Bash appends three properly-quoted strings to this snippet:Some variables are added to the environment:
COMP_LINE
contains the complete line in which completion is performed.COMP_POINT
contains the cursor position insideCOMP_LINE
(counting from 0).COMP_TYPE
is 9 for normal completion, 33 when listing alternatives on ambiguous completions, 37 for menu completion, 63 when tabbing between ambiguous completions, 64 to list completions after a partial completion.COMP_KEY
contains the key that triggered completion (e.g. a tab character if the user pressed Tab).This is roughly the same mechanism that is provided, and better documented, for functions with
-F
. Functions get a somewhat saner interface: they can read the list of words of the current command in theCOMP_WORDS
variable, and the starting position of the current word inCOMP_CWORD
; they write the results in theCOMPREPLY
array.Since the useful arguments — in particular the prefix to complete — are passed at the end of the command,
-C
is pretty much only usable with an external command. Of course you can use something likesh -c …
, but it won't be pretty.