Comment faire en sorte que bash effectue la complétion par des tabulations pour mes alias?


45

J'ai un tas de scripts d'achèvement de bash configurés (principalement à l'aide de bash-it et de certains paramétrages manuels).

J'ai aussi un tas de pseudonymes configurés pour les tâches courantes comme gcopour git checkout. À l'heure actuelle, je peux taper git checkout dTabet developest terminé pour moi, mais lorsque je tape, gco dTabcela ne se termine pas.

Je suppose que c'est parce que le script d'achèvement est en train de se terminer gitet qu'il ne voit pas gco.

Existe-t-il un moyen de faire en sorte que tous mes scripts de complétion fonctionnent avec mes alias de manière générique / par programme? Ne pas pouvoir terminer lors de l'utilisation du type d'alias annule l'objectif du pseudonyme.


Quel OS et bash utilisez-vous? Je suis sur Ubuntu 11.10 et bash 4.2.10 (1)-release (x86_64-pc-linux-gnu) et cette fonctionnalité est intégrée à mon shell pour mes nombreux alias. btw bash --versionpour obtenir cela (ne pas utiliser -v, sortie différente).
Michael Durrant

Désolé, j'ai manqué quelques informations - OSX Lion, GNU bash, version 3.2.48 (1) - release (x86_64-apple-darwin11)
dstarh

1
@killermist: à moins que je ne me trompe complètement, zsh ne complète pas non plus les commandes avec alias. Implémenter une fonction qui ajoute des alias définis à la complétion semble être beaucoup plus facile que pour bash, bien que le système de complétion de zhs semble à la fois plus puissant et simple que celui de bash.
Kopischke


1
@MichaelDurrant Êtes-vous sûr que cela est réellement construit pour les alias? Je suis sur Ubuntu 15.10 avec la version 4.3.42 (1) de Bash (x86_64-pc-linux-gnu) et rien de tel. J'ai aussi testé quelques versions précédentes. Ainsi, par exemple, si vous tapez, ll --[TAB]une liste d'options sera imprimée pour ls? Je suis assez sceptique à ce sujet, mais si vous êtes certain qu'une telle chose existait dans 11.10, je serais curieux de l'explorer et de déterminer ce qui a été supprimé.
Six

Réponses:


42

Le code suivant, adapté de cette réponse au débordement de pile et à ce fil de discussion des forums Ubuntu ajoutera des compléments à tous vos alias définis:

# 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

Pour les alias simples (commande seulement, sans arguments), il affectera la fonction d'achèvement d'origine à l'alias; pour les alias avec arguments, il crée une fonction wrapper qui insère les arguments supplémentaires dans la fonction d'achèvement d'origine.

Contrairement aux scripts qu'il a évolué à partir, la fonction respecte guillemets les deux pour la commande alias et ses arguments (mais les premiers doivent correspondre par la commande d'achèvement, et ne peut pas être imbriquées), et il devrait filtrer de manière fiable les alias des listes de commandes et les tubes (qui sont ignorés, car il est impossible de savoir quoi compléter sans recréer la logique d'analyse complète de la ligne de commande du shell).

Usage

Enregistrez le code en tant que fichier de script shell et indiquez -le dans la source , ou copiez la fonction en gros dans .bashrc(ou dans votre fichier de points pertinent ). L’important est d’appeler la fonction après que les définitions d’achèvement de bash et d’alias ont été définies (le code ci-dessus appelle la fonction juste après sa définition, dans un esprit «source et oublie», mais vous pouvez déplacer l’appel n'importe où en aval si te va mieux). Si vous ne voulez pas que la fonction soit dans votre environnement après sa fermeture, vous pouvez l'ajouter unset -f alias_completionaprès l'avoir appelée.

Remarques

Si vous utilisez la version bash4.1 ou une version ultérieure et que vous utilisez des achèvements chargés de manière dynamique, le script tente de charger les achèvement de toutes vos commandes avec alias afin de pouvoir créer les fonctions d'encapsulation pour vos alias.


1
Comment pourrais-je installer ce script?
Der Hochstapler

1
@OliverSalzburg: vous devrez le traiter dans l'un de vos fichiers de profil de shell, surtout après la finalisation de la bash - cela le ferait probablement ~/.bashrc. Stockez-le en tant que fichier de script shell et sourcez-le ( . /path/to/alias_completion.sh), ou copiez et collez le code en gros.
kopischke

1
@OliverSalzburg: ajout des instructions d'utilisation (je n'ai pas remarqué tout de suite que vous n'êtes pas l'OP).
Kopischke

1
@kopischke Voir cette question - apparemment pour les fichiers sous /usr/share/bash-completion/completions/ils ne sont chargés que la première fois que l'utilisateur tape [TAB]. Ainsi, même si la fonction est chargée à partir de ~/.bashrccelle-ci, elle ne générera pas de complétions pour les alias de commandes. Après avoir vérifié que vous complete -ptravaillez apt-getet que apt-cachej'ai copié-collé votre fonction dans le terminal, elle fonctionne correctement.
Jamadagni

1
@kopischke Donc, je ne suis pas sûr de savoir comment forcer le sourçage de tous les fichiers d'achèvement chargés de manière dynamique, ou même s'il est conseillé. Pour l' instant j'ai copié le fichier d'achèvement généré à partir /tmpde ~/.bash_completionet ajoutés manuellement à son début les pertinentes source /usr/share/bash-completion/completions/entrées (séparément apt-getet apt-cache- apt-{cache,get}ne fonctionne pas).
Jamadagni

4

Existe-t-il un moyen de faire en sorte que tous mes scripts de complétion fonctionnent avec mes alias de manière générique / par programme?

Oui, voici le projet complete-alias qui résout votre problème avec précision. Il fournit la complétion d'alias générique et programmatique sans utiliser eval.


2

Ceci est la manière manuelle, pour ceux qui cherchent cela.

Tout d'abord, recherchez la commande d'achèvement d'origine. Exemple:

$ complete | grep git

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

Ajoutez-les maintenant à votre script de démarrage (par exemple ~ / .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

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.