Come personalizzare il completamento del comando Bash?


24

In bash, è abbastanza facile impostare il completamento personalizzato degli argomenti dei comandi usando il completebuilt-in. Ad esempio, se, per un ipotetico comando con una sinossi di foo --a | --b | --c, si potesse farecomplete -W '--a --b --c' foo

È inoltre possibile personalizzare il completamento che si ottiene quando si preme Taba un vuoto pronta utilizzando complete -E, ad esempio, complete -E -W 'foo bar'. Quindi premere tab al prompt vuoto suggerirebbe solo fooe bar.

Come personalizzo il completamento del comando a un prompt non vuoto? Ad esempio, se sono seduto a:

anthony@Zia:~$ f

come personalizzare il completamento in modo da premere sempre la scheda per completare foo?

(Il caso reale che mi piacerebbe è locTABlocalc. E mio fratello, che mi ha spinto a chiederlo, lo vuole con mplayer).


2
Avete considerato semplicemente aliasing loca localc? 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.
jw013,

@ jw013 Sì, ho cercato per un po 'e non sono riuscito a trovare nulla. Mi aspetto quasi che qualcuno suggerisca di passare anche a zsh. Almeno se lo fa zsh, posso riposare comodamente sapendo anche la prossima versione di bash. 😀
derobert il

devi premere TAB due volte e completerà i comandi.
Floyd,

1
Non interromperà comunque tutti gli altri completamenti? Voglio dire, anche se è possibile, si potrebbe non essere più in grado di ottenere locate, locale, lockfileo una qualsiasi delle altre espansioni di loc. Forse un approccio migliore sarebbe quello di mappare una chiave diversa per quel completamento specifico.
terdon

1
puoi fare un esempio di tale contesto (che porterebbe all'effetto desiderato loc<TAB>->localc)?
damienfrancois,

Risposte:


9

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 -Iesattamente 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/loccon:

#!/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 | columncarry-on per visualizzare un elenco di corrispondenze).

tuttavia , un problema non banale con questo può solo usare una funzione per sostituire l'associazione alla completefunzione 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 ...


1

Non so se ho capito il tuo bisogno di questo ...
Ciò implicherebbe che il tuo bash conosce solo un comando a partire da f.
Un'idea di base del completamento è: se è ambigua, stampare le possibilità.
Quindi potresti impostare il tuoPATH su una directory contenente solo questo comando e disabilitare tutti i built-in bash per ottenere questo lavoro.

Ad ogni modo, posso darti anche una sorta di soluzione alternativa:

alias _='true &&'
complete -W foo _

Quindi, se si digita _ <Tab>, si completerà a _ fooquale viene eseguitofoo .

Tuttavia, alias f='foo'sarebbe molto più semplice.


0

Una risposta semplice per te sarebbe

$ cd into /etc/bash_completion.d
$ ls

solo le uscite di base

autoconf       gpg2               ntpdate           shadow
automake       gzip               open-iscsi        smartctl
bash-builtins  iconv              openssl           sqlite3
bind-utils     iftop              perl              ssh
brctl          ifupdown           pkg-config        strace
bzip2          info               pm-utils          subscription-manager
chkconfig      ipmitool           postfix           tar
configure      iproute2           procps            tcpdump
coreutils      iptables           python            util-linux
cpio           lsof               quota-tools       wireless-tools
crontab        lvm                redefine_filedir  xmllint
cryptsetup     lzma               rfkill            xmlwf
dd             make               rpm               xz
dhclient       man                rsync             yum.bash
e2fsprogs      mdadm              scl.bash          yum-utils.bash
findutils      module-init-tools  service
getent         net-tools          sh

basta aggiungere il programma desiderato per il completamento automatico al completamento bash


2
Questi file contengono script di shell, alcuni piuttosto complessi se ricordo bene. Inoltre, completano gli argomenti solo dopo aver digitato il comando ... Non completano il comando.
derobert,

Capisco cosa intendi. Quando puoi dare un'occhiata a questo documento: debian-administration.org/article/316/…
unixmiah,

-3

Esegui il comando seguente per trovare dove è installato il binario di mplayer:

which mplayer

O usa il percorso del binario di mplayer se lo conosci già, nel comando seguente:

ln -s /path/to/mplayer /bin/mplayer

Idealmente, qualsiasi cosa digiti viene cercata in tutte le directory specificate nella $PATHvariabile.


2
Ciao Mathew, benvenuto su Unix e Linux. Penso che la domanda del PO sia un po 'più complicata della risposta che stai suggerendo. Presumo mplayersia già nella loro $PATHe vogliono qualcosa di simile mp<tab>da produrre mplayer, invece di tutti i binari che iniziano con mp(es. mpage mpcd mpartition mplayerEcc.)
drs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.