Wie kann ich Bash dazu bringen, die Tab-Vervollständigung für meine Aliase durchzuführen?

45

Ich habe eine Reihe von Bash-Completion-Skripten eingerichtet (meistens mit Bash-It und einigen manuell erstellten Skripten).

Ich habe auch eine Reihe von Alias-Einstellungen für allgemeine Aufgaben wie gcofür git checkout. Im Moment kann ich tippen git checkout dTabund developist für mich fertig, aber wenn ich tippe, ist gco dTabes nicht fertig.

Ich gehe davon aus, dass dies daran liegt, dass das Abschlussskript abgeschlossen ist gitund nicht angezeigt wird gco.

Gibt es eine Möglichkeit, alle meine Abschlussskripte generisch / programmatisch für die Arbeit mit meinen Aliasen zu verwenden? Wenn Sie den Alias ​​nicht verwenden, können Sie den Zweck des Alias ​​aufheben.

dstarh
quelle
Welches Betriebssystem und welche Bash verwenden Sie? Ich bin auf Ubuntu 11.10 und bash 4.2.10 (1) -release (x86_64-pc-linux-gnu) und habe diese Funktionalität für meine vielen Aliase in meine Shell eingebaut. Übrigens bash --version, um dies zu bekommen (nicht verwenden -v, andere Ausgabe).
Michael Durrant
Tut mir leid, ich habe ein paar Infos verpasst - OSX Lion, GNU Bash, Version 3.2.48 (1) -Release (x86_64-apple-darwin11)
dstarh
1
@killermist: Wenn ich mich nicht völlig irre, führt zsh auch keine Alias-Befehle aus der Box aus. Das Implementieren einer Funktion, die der Vervollständigung definierte Aliase hinzufügt, scheint jedoch viel einfacher zu sein als bei bash, da das Vervollständigungssystem von zhs sowohl leistungsfähiger als auch einfacher zu sein scheint als bei bash.
kopischke
Cross site duplicate: stackoverflow.com/questions/342969/…
Ciro Santilli Am
1
@MichaelDurrant Sind Sie sicher, dass dies tatsächlich für Aliase eingebaut ist? Ich bin auf Ubuntu 15.10 mit Bash 4.3.42 (1) -Release (x86_64-pc-linux-gnu) und es gibt so etwas nicht. Ich habe auch einige frühere Versionen getestet. Wenn Sie also zum Beispiel etwas eingeben ll --[TAB], wird eine Liste mit Optionen für gedruckt ls? Ich bin ziemlich skeptisch, aber wenn Sie sicher sind, dass es so etwas in 11.10 gab, wäre ich neugierig, es zu durchforsten und herauszufinden, was entfernt wurde.
Sechs

Antworten:

42

Der folgende Code, der aus dieser Stack Overflow-Antwort und diesem Diskussionsthread der Ubuntu-Foren angepasst wurde, fügt Vervollständigungen für alle von Ihnen definierten Aliase hinzu:

# 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

Bei einfachen Aliasnamen (nur Befehl, keine Argumente) wird dem Alias ​​die ursprüngliche Vervollständigungsfunktion zugewiesen. Für Aliase mit Argumenten wird eine Wrapper-Funktion erstellt, die die zusätzlichen Argumente in die ursprüngliche Vervollständigungsfunktion einfügt.

Im Gegensatz zu den Skripten, aus denen es entwickelt wurde, werden in der Funktion Anführungszeichen sowohl für den Alias-Befehl als auch für seine Argumente berücksichtigt (ersteres muss jedoch mit dem Vervollständigungsbefehl übereinstimmen und kann nicht verschachtelt werden). Außerdem sollten Aliase zuverlässig in Befehlslisten und herausgefiltert werden Pipes (die übersprungen werden, da es unmöglich ist, herauszufinden, was in ihnen zu vervollständigen ist, ohne die gesamte Shell-Befehlszeilen-Parsing-Logik neu zu erstellen).

Verwendungszweck

Speichern Sie den Code entweder als Shell-Skriptdatei und geben Sie den Quellcode ein, oder kopieren Sie die Funktion wholesale in .bashrc(oder Ihre zugehörige Punktdatei ). Das Wichtigste ist, die Funktion aufzurufen, nachdem sowohl die Bash-Vervollständigung als auch die Alias-Definitionen eingerichtet wurden (der obige Code ruft die Funktion direkt nach ihrer Definition auf, und zwar im Sinne von „Quelle und Vergessen“) steht dir besser). Wenn Sie die Funktion nach dem Beenden nicht in Ihrer Umgebung verwenden möchten, können Sie sie unset -f alias_completionnach dem Aufrufen hinzufügen .

Anmerkungen

Wenn Sie bash4.1 oder höher verwenden und dynamisch geladene Vervollständigungen verwenden, versucht das Skript, Vervollständigungen für alle Ihre Aliasbefehle zu laden, damit die Wrapperfunktionen für Ihre Aliase erstellt werden können.

kopischke
quelle
1
Wie würde ich das Skript installieren ?
Der Hochstapler
1
@OliverSalzburg: Sie müssen es in einer Ihrer Shell-Profildateien verarbeiten, entscheidend nach der Bash-Vervollständigung - das würde es wahrscheinlich schaffen ~/.bashrc. Speichern Sie es entweder als Shell-Skriptdatei und geben Sie es als Quelle ( . /path/to/alias_completion.sh) ein, oder kopieren Sie den Code und fügen Sie ihn im Großhandel ein.
kopischke
1
@OliverSalzburg: Gebrauchsanweisung hinzugefügt (hab nicht gleich gemerkt, dass du nicht der OP bist).
kopischke
1
@kopischke Siehe diese Frage - anscheinend für die Dateien unter denen /usr/share/bash-completion/completions/sie nur das erste Mal geladen werden, wenn der Benutzer tatsächlich trifft [TAB]. Selbst wenn die Funktion von dort geladen ~/.bashrcwird, werden keine Vervollständigungen für Aliase zu Befehlen darin generiert. Nachdem sichergestellt wurde complete -p, dass für das Terminal gearbeitet wird apt-getund apt-cacheich Ihre Funktion kopiert habe, funktioniert sie ordnungsgemäß.
Jamadagni
1
@kopischke Also ich bin mir nicht sicher, wie ich das Sourcing aller dynamisch geladenen Vervollständigungsdateien erzwingen soll, oder auch wenn es ratsam ist. Im Moment habe ich die erzeugte Vervollständigungsdatei von /tmpnach kopiert ~/.bash_completionund am Anfang die entsprechenden source /usr/share/bash-completion/completions/Einträge manuell hinzugefügt (separat für apt-getund apt-cache- apt-{cache,get}funktioniert nicht).
Jamadagni
4

Gibt es eine Möglichkeit, alle meine Abschlussskripte generisch / programmatisch für die Arbeit mit meinen Aliasen zu verwenden?

Ja, hier ist das vollständige Alias- Projekt, das Ihr Problem genau löst. Es bietet allgemeine und programmatische Vervollständigung von Aliasen ohne Verwendung von eval.

Cyker
quelle
2

Dies ist der manuelle Weg für diejenigen, die dies suchen.

Rufen Sie zunächst den ursprünglichen Abschlussbefehl auf. Beispiel:

$ complete | grep git

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

Fügen Sie diese nun Ihrem Startskript hinzu (zB ~ / .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

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

weises Glück
quelle