Як я можу змусити bash виконувати заповнення вкладки для своїх псевдонімів?


45

У мене є безліч скриптів для завершення bash (в основному з використанням bash-it та деяких налаштувань вручну).

У мене також є купа аліаси установки для загальних завдань , таких як gcoдля git checkout. Зараз я можу набрати git checkout dTabта developзавершено для мене, але коли я набираю, gco dTabвін не завершує.

Я припускаю, що це тому, що сценарій завершення завершується gitі він не бачить gco.

Чи є спосіб загально / програмно отримати всі мої сценарії завершення для роботи зі своїми псевдонімами? Неможливість завершити при використанні псевдоніма вид перемагає призначення псевдоніма.


Яку ОС та bash ви використовуєте? Я перебуваю на Ubuntu 11.10 та bash 4.2.10 (1) -release (x86_64-pc-linux-gnu), і у мене є ця функціональність, вбудована в мою оболонку для моїх багатьох псевдонімів. btw, bash --versionщоб отримати це (не використовувати -v, різний вихід).
Майкл Дюрант

Вибачте, пропустили трохи інформації - OSX Lion, GNU bash, версія 3.2.48 (1) -випуск (x86_64-apple-darwin11)
dstarh

1
@killermist: якщо я зовсім не помиляюся, zsh також не виконує ззовні команди. Реалізація функції, яка додає визначені псевдоніми до завершення, виглядає набагато простіше, ніж для bash, однак, оскільки система завершення zhs здається і більш потужною, і більш простою, ніж bash.
kopischke


1
@MichaelDurrant Ви впевнені, що це насправді вбудовано для псевдонімів? Я на Ubuntu 15.10 з Bash 4.3.42 (1) -release (x86_64-pc-linux-gnu), і такого немає. Я також протестував кілька попередніх версій. Так, наприклад, якщо ви введете в ll --[TAB]ньому, буде надруковано список варіантів для ls? Я досить скептично ставлюсь до цього, але якщо ви впевнені, що таке існувало в 11.10, мені було б цікаво перекопати його та визначити, що було вилучено.
Шість

Відповіді:


42

Наступний код, адаптований з цієї відповіді на переповнення стека та цього дискусійного потоку Ubuntu Forums , додасть завершення для всіх ваших визначених псевдонімів:

# 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

Для простих псевдонімів (лише для команд, без аргументів) він присвоює псевдоніму функцію початкового завершення; для псевдонімів з аргументами він створює функцію обгортки, яка вставляє зайві аргументи в оригінальну функцію завершення.

На відміну від скриптів, з яких вона розвинулася, функція поважає цитати як для команди псевдоніму, так і для його аргументів (але перші повинні відповідати команді завершення і не можуть бути вкладені), і вона повинна надійно фільтрувати псевдоніми до списків команд і труби (які пропускаються, оскільки неможливо з'ясувати, що в них виконати, не відтворюючи логіку розбору командного рядка повного оболонки).

Використання

Або збережіть код як файл сценарію оболонки та джерело, яке знаходиться у ньому, або скопіюйте функцію оптом у .bashrc(або ваш відповідний крапковий файл ). Найважливіше - викликати функцію після того, як було встановлено як завершення bash, так і визначення псевдонімів (код вище викликає функцію відразу після її визначення, в дусі "джерело і забудь", але ви можете переміщати виклик куди завгодно нижче, якщо це вам більше підходить). Якщо ви не хочете, щоб функція була у вашому оточенні після її завершення, ви можете додати її unset -f alias_completionпісля виклику.

Примітки

Якщо ви використовуєте bash4.1 або вище та використовуєте динамічно завантажені доповнення, скрипт буде намагатися завантажити завершення для всіх ваших відчужених команд, щоб він міг будувати функції обгортки для ваших псевдонімів.


1
Як би я пішов про встановлення цього сценарію?
Der Hochstapler

1
@OliverSalzburg: вам доведеться обробити його в одному з ваших файлів профілю оболонки, головним чином після завершення bash - це, мабуть, зробить це ~/.bashrc. Або зберігайте його як файл сценарію оболонки та джерело його ( . /path/to/alias_completion.sh), або скопіюйте та вставте код оптом.
kopischke

1
@OliverSalzburg: додані інструкції з використання (не помітили відразу, що ви не ОП).
kopischke

1
@kopischke Дивіться це питання - мабуть, файли під /usr/share/bash-completion/completions/ними завантажуються лише вперше, коли користувач фактично потрапляє [TAB]. Тож навіть якщо функція завантажена з ~/.bashrcнеї, вона не буде генерувати доповнення для псевдонімів команд, що входять до неї. Після впевненості complete -p, що працює, apt-getі apt-cacheя скопіював вашу функцію в термінал, і вона працює правильно.
jamadagni

1
@kopischke Тому я не впевнений, як змусити знайти всі динамічно завантажені файли завершення, або навіть якщо це доцільно. Поки я скопіював згенерований файл завершального від /tmpдо ~/.bash_completionі вручну доданий в його початку відповідних source /usr/share/bash-completion/completions/записів (окремо для apt-getі apt-cache- apt-{cache,get}не працює).
jamadagni

4

Чи є спосіб загально / програмно отримати всі мої сценарії завершення для роботи зі своїми псевдонімами?

Так, ось повний псевдонім- проект, який точно вирішує вашу проблему. Він забезпечує завершення загального та програмного псевдоніму без використання eval.


2

Це ручний спосіб для тих, хто це шукає.

Спочатку знайдіть оригінальну команду завершення. Приклад:

$ complete | grep git

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

Тепер додайте їх до сценарію запуску (наприклад, ~ / .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

джерело: https://superuser.com/a/1004334

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.