Как создать скрипт с автозаполнением?

120

Когда я использую программу как svnи я набираю в Gnome Terminal:

svn upd

и нажмите Tabэто автозаполнение до:

svn update

Возможно ли сделать что-то подобное в моем собственном скрипте bash?

UAdapter
источник
объясните "bash script", вы имеете в виду при редактировании скрипта? что ты хочешь с этим делать?
Бруно Перейра
3
при использовании скрипта в консоли
UAdapter
Что касается места, где можно разместить свои дополнения, см. Этот вопрос, а также комментарии для принятого ответа там.
Ярно

Ответы:

44

Вы можете использовать Программируемое завершение . Посмотрите на /etc/bash_completionи /etc/bash_completion.d/*для некоторых примеров.

Флориан Диш
источник
131
Как насчет включения простого примера, непосредственно связанного с вопросом?
MountainX
2
Реальные сценарии для Ubuntu 16 расположены в/usr/share/bash-completion/completions/<program>
питер
17
Имо, примеры должны быть включены в ответ, а не в ссылку.
Billynoah
2
Я считаю, что эта платформа должна быть более практичной альтернативой полной документации, которую можно найти с помощью простого поиска в Google. Сброс ссылки на документацию не поможет. Ссылка, содержащая якорь, безусловно, не имеет большого значения.
тимусин
4
The provided link has that already- это может сегодня, но не завтра. Или в следующем году. Или через десять лет. Что бы вы ни предлагали по поводу актуальности документации, Stack Overflow не рекомендует ответы только по ссылкам по этим причинам.
Лиам Доусон
205

Я опоздал на шесть месяцев, но я искал то же самое и нашел это:

Вам нужно будет создать новый файл:

/etc/bash_completion.d/foo

Для статического автозавершения ( --help/ --verbose, например) добавить следующее:

_foo() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="--help --verbose --version"

    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _foo foo
  • COMP_WORDS массив, содержащий все отдельные слова в текущей командной строке
  • COMP_CWORD является индексом слова, содержащего текущую позицию курсора.
  • COMPREPLY переменная массива, из которой Bash читает возможные дополнения.

И compgenкоманда возвращает массив элементов --help, --verboseи --versionсоответствие текущего слова "${cur}":

compgen -W "--help --verbose --version" -- "<userinput>"

Источник: http://www.debian-administration.org/articles/316

Луи Соулез
источник
27
Это должен быть принятый ответ! Это полный пример.
Виктор Шредер
5
Совет: Если кто - то хочет , чтобы предложения по словам не начиная с -и показать им , без необходимости начать вводить целевое слово, просто удалите if [...] thenи fiлинию.
Седрик Райхенбах
1
Это точный ответ, который я искал часами, и оказывается, что он просто утонул в какой-то сложной документации, в которой просто не упоминается, что файл должен быть помещен /etc/bash_completion.d/. Я пришел сюда только потому, что хотел где-то опубликовать ответ, но оказалось, что кто-то был на три года впереди :) Ясный, лаконичный и полный пример, спасибо!
Стин Шютт
1
Учебное пособие - Создание сценария завершения bash
Lazarus Lazaridis
34

Все дополнения bash хранятся в /etc/bash_completion.d/. Поэтому, если вы создаете программное обеспечение с помощью bash_completion, было бы целесообразно, чтобы deb / make install удалил файл с именем программного обеспечения в этом каталоге. Вот пример сценария завершения bash для Rsync:

# bash completion for rsync

have rsync &&
_rsync()
{
    # TODO: _split_longopt

    local cur prev shell i userhost path   

    COMPREPLY=()
    cur=`_get_cword`
    prev=${COMP_WORDS[COMP_CWORD-1]}

    _expand || return 0

    case "$prev" in
    --@(config|password-file|include-from|exclude-from))
        _filedir
        return 0
        ;;
    -@(T|-temp-dir|-compare-dest))
        _filedir -d
        return 0
        ;;
    -@(e|-rsh))
        COMPREPLY=( $( compgen -W 'rsh ssh' -- "$cur" ) )
        return 0
        ;;
    esac

    case "$cur" in
    -*)
        COMPREPLY=( $( compgen -W '-v -q  -c -a -r -R -b -u -l -L -H \
            -p -o -g -D -t -S -n -W -x -B -e -C -I -T -P \
            -z -h -4 -6 --verbose --quiet --checksum \
            --archive --recursive --relative --backup \
            --backup-dir --suffix= --update --links \
            --copy-links --copy-unsafe-links --safe-links \
            --hard-links --perms --owner --group --devices\
            --times --sparse --dry-run --whole-file \
            --no-whole-file --one-file-system \
            --block-size= --rsh= --rsync-path= \
            --cvs-exclude --existing --ignore-existing \
            --delete --delete-excluded --delete-after \
            --ignore-errors --max-delete= --partial \
            --force --numeric-ids --timeout= \
            --ignore-times --size-only --modify-window= \
            --temp-dir= --compare-dest= --compress \
            --exclude= --exclude-from= --include= \
            --include-from= --version --daemon --no-detach\
            --address= --config= --port= --blocking-io \
            --no-blocking-io --stats --progress \
            --log-format= --password-file= --bwlimit= \
            --write-batch= --read-batch= --help' -- "$cur" ))
        ;;
    *:*)
        # find which remote shell is used
        shell=ssh
        for (( i=1; i < COMP_CWORD; i++ )); do
            if [[ "${COMP_WORDS[i]}" == -@(e|-rsh) ]]; then
                shell=${COMP_WORDS[i+1]}
                break
            fi
        done
        if [[ "$shell" == ssh ]]; then
            # remove backslash escape from :
            cur=${cur/\\:/:}
            userhost=${cur%%?(\\):*}
            path=${cur#*:}
            # unescape spaces
            path=${path//\\\\\\\\ / }
            if [ -z "$path" ]; then
                # default to home dir of specified
                # user on remote host
                path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null)
            fi
            # escape spaces; remove executables, aliases, pipes
            # and sockets; add space at end of file names
            COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
                command ls -aF1d "$path*" 2>/dev/null | \
                sed -e 's/ /\\\\\\\ /g' -e 's/[*@|=]$//g' \
                -e 's/[^\/]$/& /g' ) )
        fi
        ;;
    *)  
        _known_hosts_real -c -a "$cur"
        _filedir
        ;;
    esac

    return 0
} &&
complete -F _rsync $nospace $filenames rsync

# Local variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indent-comment: t
# indent-tabs-mode: nil
# End:
# ex: ts=4 sw=4 et filetype=sh

Вероятно, стоило бы рассмотреть один из файлов завершения bash, который наиболее точно соответствует вашей программе. Одним из самых простых примеров является rrdtoolфайл.

Марко Чеппи
источник
2
Можем ли мы настроить дополнения для загрузки из других мест? IE. ~ / .local
Крис
1
Да, вы можете поместить файл, как этот, где вы хотите, а затем положить source ~/.local/mycrazycompletionв~/.bashrc
Стефано Палаццо
@Chris см. Инструкции в Bash Completion FAQ
Ярно
В настоящее время большинство дополнений находятся в каталоге, заданном командой pkg-config --variable = завершенийdir bash-завершением`, и этот каталог является рекомендацией, приведенной выше в разделе часто задаваемых вопросов о дополнениях Bash.
Ярно
34

Вот полный учебник.

Давайте рассмотрим пример скрипта с именем admin.sh, к которому вы бы хотели, чтобы работало автозаполнение.

#!/bin/bash

while [ $# -gt 0 ]; do
  arg=$1
  case $arg in
    option_1)
     # do_option_1
    ;;
    option_2)
     # do_option_1
    ;;
    shortlist)
      echo option_1 option_2 shortlist
    ;;
    *)
     echo Wrong option
    ;;
  esac
  shift
done

Примечание вариант списка. Вызов скрипта с этой опцией распечатает все возможные опции для этого скрипта.

И вот у вас есть скрипт автозаполнения:

_script()
{
  _script_commands=$(/path/to/your/script.sh shortlist)

  local cur
  COMPREPLY=()
  cur="${COMP_WORDS[COMP_CWORD]}"
  COMPREPLY=( $(compgen -W "${_script_commands}" -- ${cur}) )

  return 0
}
complete -o nospace -F _script ./admin.sh

Обратите внимание, что последний аргумент для завершения - это имя скрипта, к которому вы хотите добавить автозаполнение. Все, что вам нужно сделать, это добавить свой скрипт автозаполнения в bashrc как

source /path/to/your/autocomplete.sh

или скопируйте его в /etc/bash.completion.d

kokosing
источник
1
Для чего prevнужна переменная? Вы, кажется, не используете это.
dimo414
@ dimo414 Кажется, я удалил эту переменную
kokosing,
Что делает -o nospaceопция?
Эндрю Ламарра
12

Если все, что вам нужно, это простое автозаполнение на основе слов (то есть без завершения подкоманды или чего-либо еще), completeкоманда имеет -Wпараметр, который просто делает правильные вещи.

Например, у меня есть следующие строки .bashrcдля автозаполнения программы под названием jupyter :

# gleaned from `jupyter --help`
_jupyter_options='console qtconsole notebook' # shortened for this answer
complete -W "${_jupyter_options}" 'jupyter'

Теперь jupyter <TAB> <TAB>автозаполнение для меня.

В документах на gnu.org полезны.

Кажется, он полагается на IFSправильную установку переменной, но это не вызвало у меня никаких проблем.

Чтобы добавить завершение имени файла и завершение BASH по умолчанию, используйте -oпараметр:

complete -W "${_jupyter_options}" -o bashdefault -o default 'jupyter'

Чтобы использовать это в zsh, добавьте следующий код перед запуском completeкоманды в вашем ~/.zshrc:

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi
Бен
источник
Как мне заставить это работать bash jupyter <TAB><TAB>?
Папампи
@papampi, я думаю, что это работает только с одним уровнем завершения - я думаю, чтобы сделать это с 2 слоями, вам понадобится один из более сложных ответов выше. Кроме того, я недавно прочитал довольно приличный урок о завершении bash. Это не делает именно то, что вам нужно, но, возможно, это поможет вам. Удачи!
Бен