Як створити сценарій з автоматичним завершенням?


119

Коли я використовую таку програму, як svnі я набираю термінал Gnome:

svn upd

і натисніть, щоб Tabвоно було завершено автоматично:

svn update

Чи можливо зробити щось подібне в моєму власному сценарії bash?


поясніть "bash script", ви маєте на увазі під час редагування сценарію? що ти хочеш з цим зробити?
Бруно Перейра

3
при використанні скрипту в консолі
UAdapter

Щодо місця, де можна скласти свої доповнення, дивіться це питання, а також коментарі до прийнятої відповіді.
jarno

Відповіді:


44

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


130
Як щодо включення простого прикладу, безпосередньо пов’язаного з питанням?
MountainX

2
Реальні сценарії для Ubuntu 16 розташовані в/usr/share/bash-completion/completions/<program>
пітер

16
Імо, приклади повинні бути включені у відповідь, а не в посилання.
billynoah

2
Я вважаю, що ця платформа повинна бути більш практичною альтернативою повним документальним документам, які можна знайти за допомогою простого пошуку в Google. Демпінг посилання на документацію не допомагає цьому. Посилання, що містить якір, безумовно, не має великого значення.
timuçin

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/. Я прийшов сюди лише тому, що хотів десь опублікувати відповідь, але виявляється, хтось був на три роки попереду весь час :) Ясний, лаконічний і повний приклад, дякую!
Стівен Шютт


34

Всі завершення башів зберігаються в /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
jarno

В даний час більшість доповнень розміщені в каталозі, заданому командою pkg-config --variable = complementsdir bash-завершення`, і цей каталог є рекомендацією, наданою FAQ, пов’язаним з Bash Completion.
jarno

33

Ось повний підручник.

Нехай є приклад сценарію під назвою 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


1
Для чого prevзмінна? Ви, схоже, не використовуєте це.
dimo414

@ dimo414 Схоже, я
змінив

Що робить -o nospaceваріант?
Ендрю Ламарра

11

Якщо все, що вам потрібно, - це просте автоматичне завершення на основі слів (тому немає завершення підкоманди чи нічого), у 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

@papampi, я думаю, що це працює лише з одним рівнем завершення - я думаю, щоб зробити це з двома шарами, вам знадобиться один із більш складних відповідей вище. Крім того, я нещодавно прочитав досить пристойний підручник про завершення баш. Він не робить саме те, що вам потрібно, але, можливо, це допоможе вам. Удачі!
Бен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.