Wie erstelle ich ein Skript mit Auto-Vervollständigung?

120

Wenn ich wie ein Programm benutze svnund Gnome Terminal eintippe:

svn upd

und drücke Tabes wird automatisch vervollständigt zu:

svn update

Kann ich so etwas in meinem benutzerdefinierten Bash-Skript tun?

UAdapter
quelle
"bash script" erklären, meinst du beim editieren eines skripts? was willst du damit machen
Bruno Pereira
3
bei Verwendung von Skripten in der Konsole
UAdapter 17.10.11
Die Stelle, an der Sie Ihre Vervollständigungen platzieren können, finden Sie in dieser Frage sowie in den Kommentaren für die akzeptierte Antwort.
Jarno

Antworten:

44

Sie können die programmierbare Vervollständigung verwenden . Haben Blick auf /etc/bash_completionund /etc/bash_completion.d/*für einige Beispiele.

Florian Diesch
quelle
131
Wie wäre es mit einem einfachen Beispiel, das direkt mit der Frage zusammenhängt?
MountainX
2
Die eigentliche Skripte für Ubuntu 16 befinden sich in/usr/share/bash-completion/completions/<program>
Peter
17
Imo, Beispiele sollten in der Antwort enthalten sein, nicht in einem Link.
Billynoah
2
Ich glaube, diese Plattform soll eine praktischere Alternative zu vollständigen Dokumentationen sein, die mit einer einfachen Google-Suche gefunden werden können. Das Speichern eines Dokumentationslinks hilft nicht weiter. Der Link, der einen Anker enthält, macht sicherlich keinen großen Unterschied.
Timuçin
4
The provided link has that already- Heute vielleicht, aber morgen vielleicht nicht. Oder nächstes Jahr. Oder in einem Jahrzehnt. Was auch immer Sie vorschlagen, dass die Dokumentation immer noch relevant ist, Stack Overflow rät aus diesen Gründen von Nur-Link-Antworten ab.
Liam Dawson
205

Ich bin ein halbes Jahr zu spät, habe aber dasselbe gesucht und folgendes herausgefunden:

Sie müssen eine neue Datei erstellen:

/etc/bash_completion.d/foo

Für eine statische Autovervollständigung ( --help/ --verbosezum Beispiel) fügen Sie Folgendes hinzu:

_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 ist ein Array, das alle einzelnen Wörter in der aktuellen Befehlszeile enthält.
  • COMP_CWORD ist ein Index des Wortes, das die aktuelle Cursorposition enthält.
  • COMPREPLY ist eine Array-Variable, aus der Bash die möglichen Vervollständigungen liest.

Und der compgenBefehl gibt das Array von Elementen zurück, aus dem das aktuelle Wort stammt --help, --verboseund --versionstimmt mit diesem überein "${cur}":

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

Quelle: http://www.debian-administration.org/articles/316

Louis Soulez
quelle
27
Dies sollte die akzeptierte Antwort sein! Es ist ein vollständiges Beispiel.
Victor Schröder
5
Tipp: Wenn jemand Vorschläge für Wörter haben möchte, die nicht mit " -und" beginnen, ohne das Zielwort eingeben zu müssen, entfernen Sie einfach die Zeilen if [...] thenund fi.
Cedric Reichenbach
1
Dies ist die genaue Antwort, nach der ich stundenlang gesucht habe, und es hat sich herausgestellt, dass sie in einer komplizierten Dokumentation ertrunken ist, in der nur nie erwähnt wird, dass die Datei abgelegt werden sollte /etc/bash_completion.d/. Ich bin nur hergekommen, weil ich irgendwo eine Antwort posten wollte, aber es stellte sich heraus, dass jemand die ganze Zeit drei Jahre voraus war :) Klares, präzises und vollständiges Beispiel, danke!
Steen Schütt
1
Tutorial - Erstellen eines Bash-Completion-Skripts
Lazarus Lazaridis
34

Alle Bash-Vervollständigungen werden in gespeichert /etc/bash_completion.d/. Wenn Sie also Software mit bash_completion erstellen, lohnt es sich, die Datei deb / make install mit dem Namen der Software in diesem Verzeichnis abzulegen. Hier ist ein Beispiel für ein Bash-Abschlussskript für 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

Es lohnt sich wahrscheinlich, eine der Bash-Abschlussdateien zu lesen, die Ihrem Programm am ehesten entspricht. Eines der einfachsten Beispiele ist die rrdtoolDatei.

Marco Ceppi
quelle
2
Können wir Vervollständigungen so konfigurieren, dass sie von anderen Standorten geladen werden? IE. ~ / .local
Chris
1
Ja, Sie können eine Datei wie diese ablegen, wo immer Sie möchten, und sie dann source ~/.local/mycrazycompletionin Ihren~/.bashrc
Stefano Palazzo
@ Chris siehe Anleitung unter Bash Completion FAQ
jarno
Heutzutage befinden sich die meisten Vervollständigungen in dem Verzeichnis, das mit dem Befehl pkg-config --variable = completionsdir bash-completion` angegeben wurde, und dieses Verzeichnis ist die Empfehlung der oben verlinkten Bash Completion FAQ.
Jarno
34

Hier ist ein komplettes Tutorial.

Nehmen wir ein Beispiel für ein Skript mit dem Namen admin.sh, an dem die automatische Vervollständigung ausgeführt werden soll.

#!/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

Optionsauswahlliste beachten. Wenn Sie das Skript mit dieser Option aufrufen, werden alle möglichen Optionen für dieses Skript ausgedruckt.

Und hier haben Sie das Autocomplete-Skript:

_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

Beachten Sie, dass das letzte zu vervollständigende Argument der Name des Skripts ist, zu dem Sie die automatische Vervollständigung hinzufügen möchten. Alles was Sie tun müssen, ist Ihr Autocomplete-Skript zu bashrc as hinzuzufügen

source /path/to/your/autocomplete.sh

oder kopieren Sie es nach /etc/bash.completion.d

kokosing
quelle
1
Wofür ist die prevVariable? Sie scheinen es nicht zu benutzen.
dimo414
@ dimo414 Anscheinend habe ich diese Variable entfernt
kokosing
Was macht die -o nospaceOption?
Andrew Lamarra
11

Wenn Sie nur eine einfache wortbasierte automatische Vervollständigung wünschen (also keine Vervollständigung von Unterbefehlen oder ähnliches), verfügt der completeBefehl über eine -WOption, die genau das Richtige tut.

Zum Beispiel habe ich die folgenden Zeilen in meinem, .bashrcum ein Programm namens jupyter automatisch zu vervollständigen :

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

Jetzt wird es jupyter <TAB> <TAB>für mich automatisch vervollständigt.

Die Dokumente auf gnu.org sind hilfreich.

Es scheint, dass die IFSVariable richtig eingestellt ist, aber das hat mir keine Probleme bereitet.

Verwenden Sie die folgende -oOption , um die Dateinamenvervollständigung und die Standard-BASH-Vervollständigung hinzuzufügen :

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

Um dies in zsh zu verwenden, fügen Sie den folgenden Code hinzu, bevor Sie den completeBefehl in Ihrem ausführen ~/.zshrc:

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi
Ben
quelle
Wie mache ich das bash jupyter <TAB><TAB>?
Papampi
@papampi, ich denke, es funktioniert nur mit einer Vervollständigungsebene - ich denke, mit 2 Ebenen müssten Sie eine der komplizierteren Antworten oben. Außerdem habe ich kürzlich ein ziemlich anständiges Tutorial über die Bash-Vervollständigung gelesen . Es macht nicht genau das, was Sie brauchen, aber vielleicht hilft es Ihnen. Viel Glück!
Ben