L'achèvement de la commande (avec d'autres choses) est géré via l' achèvement de la ligne de lecture bash . Cela fonctionne à un niveau légèrement inférieur à la «complétion programmable» habituelle (qui n'est invoquée que lorsque la commande est identifiée et les deux cas spéciaux que vous avez identifiés ci-dessus).
Mise à jour: la nouvelle version de bash-5.0 (janvier 2019) ajoute complete -I
exactement ce problème.
Les commandes readline pertinentes sont:
complete (TAB)
Attempt to perform completion on the text before point. Bash
attempts completion treating the text as a variable (if the text
begins with $), username (if the text begins with ~), hostname
(if the text begins with @), or command (including aliases and
functions) in turn. If none of these produces a match, filename
completion is attempted.
complete-command (M-!)
Attempt completion on the text before point, treating it as a
command name. Command completion attempts to match the text
against aliases, reserved words, shell functions, shell
builtins, and finally executable filenames, in that order.
De manière similaire à la plus courante complete -F
, une partie de cela peut être transférée à une fonction en utilisant bind -x
.
function _complete0 () {
local -a _cmds
local -A _seen
local _path=$PATH _ii _xx _cc _cmd _short
local _aa=( ${READLINE_LINE} )
if [[ -f ~/.complete.d/"${_aa[0]}" && -x ~/.complete.d/"${_aa[0]}" ]]; then
## user-provided hook
_cmds=( $( ~/.complete.d/"${_aa[0]}" ) )
elif [[ -x ~/.complete.d/DEFAULT ]]; then
_cmds=( $( ~/.complete.d/DEFAULT ) )
else
## compgen -c for default "command" complete
_cmds=( $(PATH=$_path compgen -o bashdefault -o default -c ${_aa[0]}) )
fi
## remove duplicates, cache shortest name
_short="${_cmds[0]}"
_cc=${#_cmds[*]} # NB removing indexes inside loop
for (( _ii=0 ; _ii<$_cc ; _ii++ )); do
_cmd=${_cmds[$_ii]}
[[ -n "${_seen[$_cmd]}" ]] && unset _cmds[$_ii]
_seen[$_cmd]+=1
(( ${#_short} > ${#_cmd} )) && _short="$_cmd"
done
_cmds=( "${_cmds[@]}" ) ## recompute contiguous index
## find common prefix
declare -a _prefix=()
for (( _xx=0; _xx<${#_short}; _xx++ )); do
_prev=${_cmds[0]}
for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
_cmd=${_cmds[$_ii]}
[[ "${_cmd:$_xx:1}" != "${_prev:$_xx:1}" ]] && break
_prev=$_cmd
done
[[ $_ii -eq ${#_cmds[*]} ]] && _prefix[$_xx]="${_cmd:$_xx:1}"
done
printf -v _short "%s" "${_prefix[@]}" # flatten
## emulate completion list of matches
if [[ ${#_cmds[*]} -gt 1 ]]; then
for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
_cmd=${_cmds[$_ii]}
[[ -n "${_seen[$_cmds]}" ]] && printf "%-12s " "$_cmd"
done | sort | fmt -w $((COLUMNS-8)) | column -tx
# fill in shortest match (prefix)
printf -v READLINE_LINE "%s" "$_short"
READLINE_POINT=${#READLINE_LINE}
fi
## exactly one match
if [[ ${#_cmds[*]} -eq 1 ]]; then
_aa[0]="${_cmds[0]}"
printf -v READLINE_LINE "%s " "${_aa[@]}"
READLINE_POINT=${#READLINE_LINE}
else
: # nop
fi
}
bind -x '"\C-i":_complete0'
Cela permet à votre propre chaîne de commande ou préfixe de se connecter ~/.complete.d/
. Par exemple, si vous créez un exécutable ~/.complete.d/loc
avec:
#!/bin/bash
echo localc
Cela fera (à peu près) ce que vous attendez.
La fonction ci-dessus va à certaines longueurs pour émuler le comportement normal d'achèvement de la commande bash, bien qu'il soit imparfait (en particulier le bagage à sort | fmt | column
main douteux pour afficher une liste de correspondances).
Cependant , un problème non trivial avec cela, il ne peut utiliser qu'une fonction pour remplacer la liaison à la complete
fonction principale (invoquée avec TAB par défaut).
Cette approche fonctionnerait bien avec une liaison de touches différente utilisée uniquement pour la complétion de commande personnalisée, mais elle n'implémente tout simplement pas la logique de complétion complète après cela (par exemple, des mots ultérieurs dans la ligne de commande). Pour ce faire, il faudrait analyser la ligne de commande, gérer la position du curseur et d'autres choses délicates qui ne devraient probablement pas être prises en compte dans un script shell ...
loc
àlocalc
? Je suggère des alternatives car après un certain temps à creuser et à chercher, je n'ai pas trouvé un moyen de personnaliser l'achèvement de bash de cette façon. Ce n'est peut-être pas possible.