Jak mogę uzyskać bash, aby wykonać uzupełnianie tabulatorami dla moich aliasów?

45

Mam skonfigurowanych kilka skryptów kończących bash (głównie przy użyciu bash-it i niektórych ustawień ręcznych).

Mam też kilka aliasów konfiguracji dla typowych zadań, takich jak gcona git checkout. W tej chwili mogę pisać git checkout dTabi developjest dla mnie kompletny, ale kiedy piszę gco dTab, nie jest kompletny.

Zakładam, że dzieje się tak, ponieważ skrypt ukończenia jest włączony giti nie można go zobaczyć gco.

Czy istnieje sposób, aby generalnie / programowo sprawić, aby wszystkie moje skrypty uzupełniania działały z moimi aliasami? Nie można ukończyć, gdy używasz aliasu, pokonując cel aliasu.

dstarh
źródło
Jakiego systemu operacyjnego i bash używasz? Korzystam z systemu Ubuntu 11.10 i wersji bash 4.2.10 (1) (x86_64-pc-linux-gnu) i mam tę funkcję wbudowaną w moją powłokę dla wielu aliasów. btw, bash --versionaby uzyskać to (nie używaj -v, inne dane wyjściowe).
Michael Durrant
Przepraszam, brakowało mi trochę informacji - OSX Lion, GNU bash, wersja 3.2.48 (1) -release (x86_64-apple-darwin11)
dstarh
1
@killermist: chyba, że ​​się całkowicie mylę, zsh również nie wykonuje gotowych poleceń. Wdrożenie funkcji, która dodaje zdefiniowane aliasy do zakończenia, wydaje się jednak znacznie łatwiejsze niż w przypadku bash, ponieważ system uzupełniania zhs wydaje się zarówno bardziej wydajny, jak i prostszy niż bash.
kopischke
Duplikat krzyżowy: stackoverflow.com/questions/342969/…
Ciro Santilli 新疆 改造 中心 996ICU 六四 事件
1
@MichaelDurrant Czy na pewno jest to wbudowane w aliasy? Jestem na Ubuntu 15.10 z wydaniem Bash 4.3.42 (1) (x86_64-pc-linux-gnu) i nie ma czegoś takiego. Testowałem także kilka wcześniejszych wydań. Na przykład, jeśli wpiszesz ll --[TAB], wydrukuje listę opcji ls? Jestem dość sceptyczny, ale jeśli jesteś pewien, że coś takiego istniało w 11.10, byłbym ciekawy, aby to przejrzeć i ustalić, co zostało usunięte.
Sześć

Odpowiedzi:

42

Poniższy kod, zaadaptowany z tej odpowiedzi przepełnienia stosu i wątku dyskusji na forach Ubuntu , doda uzupełnienia dla wszystkich zdefiniowanych aliasów:

# 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

W przypadku prostych aliasów (tylko komenda, bez argumentów) przypisze oryginalną funkcję uzupełniania do aliasu; dla aliasów z argumentami tworzy funkcję otoki, która wstawia dodatkowe argumenty do oryginalnej funkcji uzupełniania.

W przeciwieństwie do skryptów, z których ewoluował, funkcja respektuje cudzysłowy zarówno dla polecenia aliasu, jak i jego argumentów (ale te pierwsze muszą być dopasowane przez polecenie zakończenia i nie mogą być zagnieżdżone), i powinno niezawodnie filtrować aliasy do list poleceń i potoki (które są pomijane, ponieważ nie można dowiedzieć się, co należy w nich wykonać bez ponownego utworzenia kompletnej logiki parsowania wiersza poleceń powłoki).

Stosowanie

Zapisz kod jako plik skryptu powłoki i źródło , w którym się znajduje, lub skopiuj funkcję hurtowo do .bashrc(lub odpowiedniego pliku kropki ). Ważne jest, aby wywołać funkcję po skonfigurowaniu zarówno zakończenia basha, jak i definicji aliasu (powyższy kod wywołuje funkcję zaraz po jej zdefiniowaniu, w duchu „źródła i zapomnienia”, ale można przenieść wywołanie gdziekolwiek poniżej) bardziej Ci odpowiada). Jeśli nie chcesz, aby funkcja w twoim środowisku po jej wyjściu, możesz dodać unset -f alias_completionpo wywołaniu.

Notatki

Jeśli używasz wersji bash4.1 lub nowszej i używasz dynamicznie ładowanych uzupełnień, skrypt podejmie próbę załadowania uzupełnień dla wszystkich twoich aliasowanych poleceń, aby móc zbudować funkcje otoki dla twoich aliasów.

kopischke
źródło
1
Jak powinienem zainstalować ten skrypt?
Der Hochstapler
1
@OliverSalzburg: będziesz musiał przetworzyć go w jednym z plików profilu powłoki, co jest szczególnie ważne po zakończeniu bash - prawdopodobnie by to się udało ~/.bashrc. Zapisz go jako plik skryptu powłoki i zgłoś go ( . /path/to/alias_completion.sh) lub skopiuj i wklej kod hurtowo.
kopischke
1
@OliverSalzburg: dodano instrukcje użytkowania (nie zauważyłem od razu, że nie jesteś OP).
kopischke
1
@kopischke Zobacz to pytanie - najwyraźniej dla plików pod /usr/share/bash-completion/completions/nimi są ładowane tylko za pierwszym razem, gdy użytkownik faktycznie trafi [TAB]. Więc nawet jeśli funkcja jest załadowana z ~/.bashrcniego, nie wygeneruje uzupełnień dla aliasów do poleceń w nim zawartych. Po upewnieniu complete -psię, że działa apt-geti apt-cacheskopiowałem twoją funkcję do terminala i działa poprawnie.
jamadagni
1
@kopischke Nie jestem więc pewien, jak wymusić pozyskiwanie wszystkich dynamicznie ładowanych plików ukończenia, a nawet jeśli jest to wskazane. Na razie mam skopiowane wygenerowany plik kompletację od /tmpcelu ~/.bash_completioni ręcznie dodane na jego początku odpowiednie source /usr/share/bash-completion/completions/wpisy (osobno dla apt-geti apt-cache- apt-{cache,get}nie działa).
jamadagni
4

Czy istnieje sposób, aby generalnie / programowo sprawić, aby wszystkie moje skrypty uzupełniania działały z moimi aliasami?

Tak, oto projekt kompletnego aliasu , który dokładnie rozwiązuje Twój problem. Zapewnia ogólne i programowe uzupełnianie aliasów bez użycia eval.

Cyker
źródło
2

Jest to ręczny sposób dla tych, którzy tego szukają.

Najpierw wyszukaj oryginalne polecenie zakończenia. Przykład:

$ complete | grep git

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

Teraz dodaj je do skryptu uruchamiania (np. ~ / .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

źródło: https://superuser.com/a/1004334

wisbucky
źródło