How to write autocomplete in bash, so that if I have:
mycommand first_argument|garbage
where |
denotes the cursor, it should pass "first_argument"
and not "first_argumentgarbage"
to compgen?
In the examples I have it behaves in the wrong way
COMPREPLY=( $(compgen -W "add remove list use current" -- "$cur") ) # buggy
Bash completion uses a lot of different variables. Some of them are used to process input and determine which parameter to complete.
For the following explanation, I will use this test input (with |
as the cursor) :
./test.sh ad re|garbage
${COMP_WORDS}
: contains all the words of the input in the form of an array. In this case, it contains : ${COMP_WORDS[@]} == {"./test.sh", "ad", "regarbage"}
$COMP_WORDBREAKS
variable$COMP_CWORD
: contains the position of the word the cursor is curently selecting. In this case, it contains : $COMP_CWORD == 2
$COMP_LINE
: contains the whole input in form of string. In this case, it contains : $COMP_LINE == "./test.sh ad regarbage"
$COMP_POINT
: contains the position of the cursor in the whole line. In this case, it contains : $COMP_POINT == 15
Still using the same data, doing cur=${COMP_WORDS[COMP_CWORD]}
will return the element at index 2 in the ${COMP_WORD}
array, which is regarbage
.
To circumvent this behaviour, you'll have to play around with the $COMP_LINE
and $COMP_POINT
variables as well. Here is what I came up with :
# we truncate our line up to the position of our cursor
# we transform the result into an array
cur=(${COMP_LINE:0:$COMP_POINT})
# we use ${cur} the same way we would use ${COMP_WORDS}
COMPREPLY=( $( compgen -W "add remove list use current" -- "${cur[$COMP_CWORD]}" ) )
Output :
> ./test2.sh ad re|garbage
# press TAB
> ./test2.sh ad remove|garbage
Note that by default, there will be no space between remove
and garbage
. You'll have to play around the completion mechanics if this is a behaviour you want.