Il completamento del comando (insieme ad altre cose) viene gestito tramite il completamento basline readline . Funziona a un livello leggermente inferiore rispetto al solito "completamento programmabile" (che viene invocato solo quando viene identificato il comando e i due casi speciali identificati sopra).
Aggiornamento: la nuova versione di bash-5.0 (gennaio 2019) aggiunge complete -I
esattamente questo problema.
I comandi readline rilevanti sono:
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.
In modo simile al più comune complete -F
, parte di questo può essere passato a una funzione usando 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'
Questo abilita i tuoi hook di stringa per comando o prefisso ~/.complete.d/
. Ad esempio, se si crea un eseguibile ~/.complete.d/loc
con:
#!/bin/bash
echo localc
Questo farà (approssimativamente) quello che ti aspetti.
La funzione di cui sopra si estende fino ad un certo punto per emulare il normale comportamento di completamento del comando bash, anche se è imperfetto (in particolare il dubbio sort | fmt | column
carry-on per visualizzare un elenco di corrispondenze).
tuttavia , un problema non banale con questo può solo usare una funzione per sostituire l'associazione alla complete
funzione principale (invocata con TAB per impostazione predefinita).
Questo approccio funzionerebbe bene con una diversa associazione di tasti utilizzata solo per il completamento di comandi personalizzati, ma semplicemente non implementa la logica di completamento completo (ad es. Parole successive nella riga di comando). Ciò richiederebbe di analizzare la riga di comando, occuparsi della posizione del cursore e altre cose difficili che probabilmente non dovrebbero essere considerate in uno script di shell ...
loc
alocalc
? Suggerisco alternative perché dopo un po 'di tempo scavando e cercando non ho trovato un modo per personalizzare il completamento della bash in questo modo. Potrebbe non essere possibile.