Issue
I'm writing a custom completion function for a bash script (this script is for activating deactivating nginx configuration files). I'm trying to have a different behavior if the completed word starts with --
, is empty, or starts with something else. Typically, if I write command --<TAB>
, I should have --help --someoptions
suggested, whereas if I type command <TAB>
then I should have the content of a specific directory, matching a specific regex, shown as completion suggestions.
Here is the code of my completion function:
#!/bin/bash
source "./common"
_comp_nginx_conf(){
local IFS=$' '
local cur long_help conf_files new_help
cur=
long_help=( "help" "test" "testmode" "deactivate" "activate" )
# for correct completion, check if test mode is in the options
for arg in "${COMP_WORDS[@]}"; do
if [[ "$arg" == "--test" || "$arg" == "-t" ]]; then
TEST_MODE=true
fi
done
[[ $TEST_MODE ]] && cd "$TEST_PATH" &> /dev/null || cd "$PROD_PATH" &> /dev/null
conf_files=$(ls -b *(.conf|.conf.incative) 2> /dev/null)
new_help=()
COMPREPLY=()
cur="$2"
if [[ $cur =~ "--".* ]]; then
COMPREPLY=( $(compgen -W "${long_help[*]}" -P "--" -- $cur) )
else
COMPREPLY=( $(compgen -W "${conf_files[*]}" -- $cur) )
fi
return 0
} &&
complete -F _comp_nginx_conf nginx-conf
The issue here is, when I type command --<TAB>
it should offer --help --testmode --deactivate --activate
and when I type command <TAB>
it should offer test.conf test2.conf.inactive
and all the conf files in $TEST_PATH
, but instead it still offers --someoptions
.
I don't understand why I get this result, so this is where I need help, how to achieve the behavior I want and why don't I get it with the code I wrote?
Solution
I think I find your function too complicated. *(...)
is an extended glob. For the -P --
you have to ${cur%--}
remove the dashes, otherwise they won't match. And I do not get the "TEST_MODE" - why would you want confuse users by changing directory in completion function! I use set -x
for testing. Also function definition always succeeds, why &&
of it.
_comp_nginx_conf() {
local cur
cur=$2
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W "--help --test --testmode --deactivate --activate" -- $cur))
else
COMPREPLY=($(
compgen -G '*.conf' -- $cur
compgen -G '*.conf.incative' -- $cur
))
fi
}
complete -F _comp_nginx_conf nginx-conf
Overall, why care at all, compgen will just return nothing:
COMPREPLY=($(
compgen -W "--help --test --testmode --deactivate --activate" -- $cur
compgen -G '*.conf' -- $cur
compgen -G '*.conf.incative' -- $cur
))
If you want to complete files from some directory, change the directory in the subshell. I would be very confused if my PWD would change when executing completion.
local dir IFS=" "
case " ${COMP_WORDS[*]} " in
*" --test "*|*" -t "*) dir=$TEST_PATH; ;;
*) dir=$PROD_PATH; ;;
esac
COMPREPLY=($(
cd "$dir" 2>/dev/null
compgen -G '*.conf' -- $cur
compgen -G '*.conf.incative' -- $cur
))
Check your scripts with shellcheck. Prefer if
over && ||
.
Answered By - KamilCuk Answer Checked By - Pedro (WPSolving Volunteer)