How to prevent bash completion from replacing a character when tab completing
bash, bash-completion
Solution
So, this intrigued me, so I've been reading through the bash completion source (available at /etc/bash_completion).
I came across this variable: `${COMP_WORDBREAKS}`, which appears to allow control over what characters are used to delimit words.
I also came across this function, `_get_cword` and the complementary `_get_pword`, both recommended for use in place of `${COMP_WORDS[COMP_CWORD]}` and `${COMP_WORDS[COMP_CWORD-1]}` respectively.
So, putting all this together, I did some testing and here's what I've come up with: this seems to work for me, at least, hopefully it will for you too:
# maintain the old value, so that we only affect ourselves with this
OLD_COMP_WORDBREAKS=${COMP_WORDBREAKS}
COMP_WORDBREAKS="${COMP_WORDBREAKS}@"
cur="$(_get_cword)"
prev="$(_get_pword)"
if [[ "$cur" == '=@' ]]; then
COMPREPLY=( $(compgen -f ${cur:2}) )
# restore the old value
COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
return 0
fi
if [[ "$prev" == '=@' ]]; then
COMPREPLY=( $(compgen -f ${cur}) )
# restore the old value
COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
return 0
fi
Now, I'll admit that the split `if` cases are a little dirty, there's definitely a better way to do it but I need more caffeine.
Further, for whatever it's worth to you I also discovered along the way the `-P <prefix>` argument to `compgen`, which would prevent you from having to loop over the `$COMPREPLY[*]}` array after calling `compgen`, like so
COMPREPLY=( $(compgen -P @ -f ${cur:1}) )
That's a little redundant in the face of a full solution, though.
Problem
I'm building a bash completion script for a tool which shares file uploading semantics with curl. With curl, you can do: curl -F var=@file to upload a file. My application has similar semantics and I wish to be able to show possible files after the '@' is pressed. Unfortunately, this is proving difficult: ``` cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [[ "$cur" == @* && "$prev" == '=' ]]; then COMPREPLY=( $(compgen -f ${cur:1}) ) return 0 fi ``` So if a command (so far) ends with: ``` abc=@ ``` Files in the current directory will show. ``` var=@/usr/ /usr/bin /usr/games ``` Problem is if I actually hit tab to complete, the '@' goes away! ``` var=/usr/bin ``` So it looks like bash replaces the entire current word with the tabbed COMPREPLY. The only way to avoid this has been to do this: ``` COMPREPLY=( $(compgen -f ${cur:1}) ) for (( i=0; i<${#COMPREPLY[@]}; i++ )); do COMPREPLY[$i]='@'${COMPREPLY[$i]} done ``` But now the tab completion looks weird to say the least: ``` @/usr/bin @/usr/games ``` Is there anyway to show a normal file tab completion (without the '@' prefix) but preserve the '@' when hitting tab?