Come posso ottenere bash per eseguire il completamento di tabulazione per i miei alias?


45

Ho un sacco di script di completamento bash impostati (principalmente usando bash-it e alcuni impostati manualmente).

Ho anche un sacco di alias impostati per attività comuni come gcoper git checkout. In questo momento posso digitare git checkout dTabed developè completo per me, ma quando scrivo gco dTabnon si completa.

Suppongo che ciò avvenga perché lo script di completamento è in fase di completamento gite non riesce a vedere gco.

C'è un modo per far funzionare genericamente / programmaticamente tutti i miei script di completamento con i miei alias? Non essere in grado di completare quando si utilizza il tipo di alias di sconfitte lo scopo dell'alias.


Quale sistema operativo e bash stai usando? Sono su Ubuntu 11.10 e bash 4.2.10 (1) -release (x86_64-pc-linux-gnu) e ho questa funzionalità integrata nella mia shell per i miei numerosi alias. btw bash --versionper ottenere questo (non usare -v, output diverso).
Michael Durrant,

Mi dispiace aver perso un po 'di informazioni - OSX Lion, GNU bash, versione 3.2.48 (1) -release (x86_64-apple-darwin11)
dstarh

1
@killermist: a meno che non mi sbagli completamente, zsh non completa nemmeno i comandi con alias. L'implementazione di una funzione che aggiunge alias definiti al completamento sembra essere molto più semplice che per bash, poiché il sistema di completamento di zhs sembra sia più potente che più semplice di quello di bash.
kopischke,


1
@MichaelDurrant Sei sicuro che questo sia effettivamente incorporato per gli alias? Sono su Ubuntu 15.10 con Bash 4.3.42 (1) -release (x86_64-pc-linux-gnu) e non esiste nulla del genere. Ho anche testato alcune versioni precedenti. Ad esempio, se si digita ll --[TAB], verrà stampato un elenco di opzioni per ls? Sono piuttosto scettico di questo, ma se sei sicuro che una cosa del genere sia esistita nell'11.10, sarei curioso di scavare e determinare cosa è stato rimosso.
Sei

Risposte:


42

Il seguente codice, adattato da questa risposta Stack Overflow e da questo thread di discussione sui forum di Ubuntu aggiungerà completamenti per tutti i tuoi alias definiti:

# Automatically add completion for all aliases to commands having completion functions
function alias_completion {
    local namespace="alias_completion"

    # parse function based completion definitions, where capture group 2 => function and 3 => trigger
    local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)'
    # parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments
    local alias_regex="alias ([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'"

    # create array of function completion triggers, keeping multi-word triggers together
    eval "local completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))"
    (( ${#completions[@]} == 0 )) && return 0

    # create temporary file for wrapper functions and completions
    rm -f "/tmp/${namespace}-*.tmp" # preliminary cleanup
    local tmp_file; tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}XXX.tmp")" || return 1

    local completion_loader; completion_loader="$(complete -p -D 2>/dev/null | sed -Ene 's/.* -F ([^ ]*).*/\1/p')"

    # read in "<alias> '<aliased command>' '<command args>'" lines from defined aliases
    local line; while read line; do
        eval "local alias_tokens; alias_tokens=($line)" 2>/dev/null || continue # some alias arg patterns cause an eval parse error
        local alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }"

        # skip aliases to pipes, boolean control structures and other command lists
        # (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters)
        eval "local alias_arg_words; alias_arg_words=($alias_args)" 2>/dev/null || continue
        # avoid expanding wildcards
        read -a alias_arg_words <<< "$alias_args"

        # skip alias if there is no completion function triggered by the aliased command
        if [[ ! " ${completions[*]} " =~ " $alias_cmd " ]]; then
            if [[ -n "$completion_loader" ]]; then
                # force loading of completions for the aliased command
                eval "$completion_loader $alias_cmd"
                # 124 means completion loader was successful
                [[ $? -eq 124 ]] || continue
                completions+=($alias_cmd)
            else
                continue
            fi
        fi
        local new_completion="$(complete -p "$alias_cmd")"

        # create a wrapper inserting the alias arguments if any
        if [[ -n $alias_args ]]; then
            local compl_func="${new_completion/#* -F /}"; compl_func="${compl_func%% *}"
            # avoid recursive call loops by ignoring our own functions
            if [[ "${compl_func#_$namespace::}" == $compl_func ]]; then
                local compl_wrapper="_${namespace}::${alias_name}"
                    echo "function $compl_wrapper {
                        (( COMP_CWORD += ${#alias_arg_words[@]} ))
                        COMP_WORDS=($alias_cmd $alias_args \${COMP_WORDS[@]:1})
                        (( COMP_POINT -= \${#COMP_LINE} ))
                        COMP_LINE=\${COMP_LINE/$alias_name/$alias_cmd $alias_args}
                        (( COMP_POINT += \${#COMP_LINE} ))
                        $compl_func
                    }" >> "$tmp_file"
                    new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }"
            fi
        fi

        # replace completion trigger by alias
        new_completion="${new_completion% *} $alias_name"
        echo "$new_completion" >> "$tmp_file"
    done < <(alias -p | sed -Ene "s/$alias_regex/\1 '\2' '\3'/p")
    source "$tmp_file" && rm -f "$tmp_file"
}; alias_completion

Per alias semplici (solo comando, senza argomenti) assegnerà la funzione di completamento originale all'alias; per gli alias con argomenti, crea una funzione wrapper che inserisce gli argomenti extra nella funzione di completamento originale.

A differenza degli script da cui si è evoluta, la funzione rispetta le virgolette sia per il comando alias che per i suoi argomenti (ma il primo deve essere abbinato al comando di completamento e non può essere nidificato) e deve filtrare in modo affidabile gli alias in elenchi di comandi e tubi (che sono saltati, è impossibile trovare cosa per completare in loro senza ricreare la logica di comando di shell analisi completa).

uso

Salvare il codice come file di script della shell e sorgente in quello, oppure copiare la funzione all'ingrosso in, .bashrc(o il proprio file punto pertinente ). L'importante è chiamare la funzione dopo aver impostato sia il completamento bash sia le definizioni alias (il codice sopra chiama la funzione subito dopo la sua definizione, in uno spirito di "fonte e dimentica", ma puoi spostare la chiamata ovunque a valle se ti sta meglio). Se non si desidera la funzione nel proprio ambiente dopo la sua uscita, è possibile aggiungere unset -f alias_completiondopo averla chiamata.

Appunti

Se si utilizza bash4.1 o versioni successive e si utilizzano completamenti caricati dinamicamente, lo script tenterà di caricare i completamenti per tutti i comandi con alias in modo da poter creare le funzioni wrapper per i propri alias.


1
Come potrei fare per installare quello script?
Der Hochstapler,

1
@OliverSalzburg: dovrai elaborarlo in uno dei tuoi file del profilo della shell, in modo cruciale dopo il completamento della bash - probabilmente lo farebbe ~/.bashrc. O archiviarlo come file di script di shell e sorgente ( . /path/to/alias_completion.sh), oppure copiare e incollare il codice all'ingrosso.
kopischke,

1
@OliverSalzburg: aggiunte istruzioni d'uso (non ho notato subito che non sei il PO).
kopischke,

1
@kopischke Vedi questa domanda - apparentemente per i file sotto di /usr/share/bash-completion/completions/essi vengono caricati solo la prima volta che l'utente colpisce effettivamente [TAB]. Quindi, anche se la funzione viene caricata da ~/.bashrcessa, non genererà completamenti per gli alias ai comandi in essa contenuti. Dopo aver assicurato complete -psta lavorando per apt-gete apt-cachecopio-incollato la funzione al terminale e sta funzionando correttamente.
Jamadagni,

1
@kopischke Quindi non sono sicuro di come forzare l'approvvigionamento di tutti i file di completamento caricati dinamicamente, o anche se è consigliabile. Per ora ho copiato il file di completamento generato da /tmpa ~/.bash_completione ha aggiunto manualmente al suo inizio le relative source /usr/share/bash-completion/completions/voci (separatamente per apt-gete apt-cache- apt-{cache,get}non funziona).
Jamadagni,

4

C'è un modo per far funzionare genericamente / programmaticamente tutti i miei script di completamento con i miei alias?

Sì, ecco il progetto alias completo che risolve esattamente il tuo problema. Fornisce il completamento di alias generico e programmatico senza l'utilizzo eval.


2

Questo è il modo manuale, per chi lo sta cercando.

Innanzitutto, cerca il comando di completamento originale. Esempio:

$ complete | grep git

complete -o bashdefault -o default -o nospace -F __git_wrap__git_main git

Ora aggiungili allo script di avvio (ad es. ~ / .Bashrc):

# load dynamically loaded completion functions (may not be required)
_completion_loader git

# copy the original statement, but replace the last command (git) with your alias (g)
complete -o bashdefault -o default -o nospace -F __git_wrap__git_main g

fonte: https://superuser.com/a/1004334

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.