You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.6 KiB
3.6 KiB
Recursive completion — call container's own bash completion
The idea: instead of reimplementing completion, delegate to bash inside the container and get back its own completion results.
The approach
_incus_exec_delegate() {
local cur="${COMP_WORDS[COMP_CWORD]}"
local container="${COMP_WORDS[2]}"
# find '--' position
local dashdash_pos=0
local i
for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do
[[ "${COMP_WORDS[$i]}" == "--" ]] && dashdash_pos=$i
done
# complete container names
if (( COMP_CWORD == 2 )); then
COMPREPLY=($(compgen -W \
"$(incus list --format csv --columns n 2>/dev/null)" \
-- "$cur"))
return
fi
# before '--'
if (( dashdash_pos == 0 )); then
COMPREPLY=($(compgen -W "-- --env --user --cwd" -- "$cur"))
return
fi
# after '--' — delegate full completion to container's bash
local inner_words=("${COMP_WORDS[@]:$((dashdash_pos + 1))}")
local inner_cword=$(( COMP_CWORD - dashdash_pos - 1 ))
local inner_cmd="${inner_words[0]}"
local inner_line="${inner_words[*]}"
# ask container's bash to run completion
COMPREPLY=($(incus exec "$container" -- bash -c "
# load bash completion
source /usr/share/bash-completion/bash_completion 2>/dev/null
source /etc/bash_completion 2>/dev/null
# rebuild COMP_ variables inside container
COMP_WORDS=(${inner_words[*]@Q})
COMP_CWORD=$inner_cword
COMP_LINE=${inner_line@Q}
COMP_POINT=\${#COMP_LINE}
# find and call the completion function for the command
local compfunc
compfunc=\$(complete -p ${inner_cmd@Q} 2>/dev/null \
| grep -oP '(?<=-F )\S+')
if [[ -n \"\$compfunc\" ]]; then
\$compfunc ${inner_cmd@Q} ${cur@Q} ${inner_words[$((inner_cword - 1))]@Q}
printf '%s\n' \"\${COMPREPLY[@]}\"
else
# fallback to file completion
compgen -f -- ${cur@Q}
fi
" 2>/dev/null))
}
complete -F _incus_exec_delegate incus
How it works
host$ incus exec mycontainer -- git che<TAB>
↑
inner_cmd="git"
↓
incus exec mycontainer -- bash -c '
source /usr/share/bash-completion/bash_completion
COMP_WORDS=(git che)
COMP_CWORD=1
compfunc=$(complete -p git | grep -oP "(?<=-F )\S+")
$compfunc ... # calls __git_wrap__git_main
printf "%s\n" "${COMPREPLY[@]}"
'
↓
COMPREPLY=(checkout cherry cherry-pick)
Caveats
# completion inside container requires bash-completion installed
incus exec mycontainer -- apt install -y bash-completion
# verify completion available for a command
incus exec mycontainer -- bash -c "complete -p git"
Fallback chain
container has completion function for cmd?
↓ yes → delegate to it
↓ no → compgen -c (commands)
↓
compgen -f (files)
Quick reference
| Step | What happens |
|---|---|
rebuild COMP_WORDS |
strip incus exec CTR -- prefix |
complete -p CMD |
find completion function in container |
$compfunc |
call it with correct args |
| fallback | compgen -f for file completion |
The key insight is rebuilding COMP_WORDS and COMP_CWORD with the incus exec CTR -- prefix stripped — the container's bash then sees the command line exactly as if it were running locally.