Come creare uno script con il completamento automatico?


120

Quando uso un programma simile svne digito Gnome Terminal:

svn upd

e premi Tabè completato automaticamente per:

svn update

È possibile fare qualcosa del genere nel mio script bash personalizzato?


spiega "bash script", intendi quando modifichi uno script? che cosa vuoi fare con esso?
Bruno Pereira,

3
quando si utilizza lo script nella console
UAdapter,

Per quanto riguarda il luogo in cui inserire i tuoi completamenti, vedi questa domanda e anche i commenti per la risposta accettata lì.
Jarno

Risposte:


44

È possibile utilizzare il completamento programmabile . Dai un'occhiata /etc/bash_completione /etc/bash_completion.d/*per alcuni esempi.


131
Che ne dici di includere un semplice esempio direttamente correlato alla domanda?
MountainX,

2
Gli script effettivi per Ubuntu 16 si trovano in/usr/share/bash-completion/completions/<program>
peter il

17
Imo, gli esempi dovrebbero essere inclusi nella risposta, non in un collegamento.
Billynoah,

2
Credo che questa piattaforma dovrebbe essere un'alternativa più pratica alla documentazione completa che potrebbe essere trovata con una semplice ricerca su Google. Scaricare un collegamento alla documentazione non aiuta. Il link contenente un'ancora sicuramente non fa molta differenza.
timuçin,

4
The provided link has that already- potrebbe essere oggi, ma potrebbe non domani. O l'anno prossimo. O tra un decennio. Qualunque cosa tu possa suggerire sulla documentazione ancora pertinente, Stack Overflow scoraggia le risposte di solo collegamento per questi motivi.
Liam Dawson,

205

Sono in ritardo di sei mesi ma stavo cercando la stessa cosa e ho trovato questo:

Dovrai creare un nuovo file:

/etc/bash_completion.d/foo

Per un completamento automatico statico ( --help/ --verbosead esempio) aggiungi questo:

_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 è un array che contiene tutte le singole parole nella riga di comando corrente.
  • COMP_CWORD è un indice della parola contenente la posizione corrente del cursore.
  • COMPREPLY è una variabile di matrice da cui Bash legge i possibili completamenti.

E il compgencomando restituisce la matrice di elementi --help, --verbosee --versioncorrispondenti la parola corrente "${cur}":

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

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


27
Questa dovrebbe essere la risposta accettata! È un esempio completo.
Victor Schröder,

5
Suggerimento: se qualcuno desidera suggerimenti per le parole che non iniziano con -e mostrarli senza dover iniziare a digitare la parola target, basta rimuovere le righe if [...] thene fi.
Cedric Reichenbach,

1
Questa è la risposta esatta che stavo cercando da ore, e si scopre che è appena annegato in una documentazione complicata che non menziona mai che il file dovrebbe essere inserito /etc/bash_completion.d/. Sono venuto qui solo perché volevo pubblicare una risposta da qualche parte, ma si è scoperto che qualcuno era avanti di tre anni :) Esempio chiaro, conciso e completo, grazie!
Steen Schütt,


34

Tutti i completamenti di bash sono memorizzati in /etc/bash_completion.d/. Quindi, se stai creando software con bash_completion, varrebbe la pena che il deb / make install rilasci un file con il nome del software in quella directory. Ecco un esempio di script di completamento bash per 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

Sarebbe probabilmente utile rivedere uno dei file di completamento di bash che si avvicinano maggiormente al tuo programma. Uno degli esempi più semplici è il rrdtoolfile.


2
Possiamo configurare i completamenti da caricare da altre posizioni? IE. ~ / .local
Chris

1
Sì, puoi mettere un file come questo dove vuoi e poi inserire il source ~/.local/mycrazycompletiontuo~/.bashrc
Stefano Palazzo

@Chris vedi le istruzioni alle Domande frequenti sul completamento di Bash
jarno

Oggi la maggior parte dei completamenti si trova nella directory data dal comando pkg-config --variable = completionsdir bash-completamento` e quella directory è la raccomandazione data dalle Domande frequenti sul completamento di Bash collegate sopra.
Jarno

34

Ecco un tutorial completo.

Consente di avere un esempio di script chiamato admin.sh in cui si desidera far funzionare il completamento automatico.

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

Lista delle opzioni nota. La chiamata allo script con questa opzione stamperà tutte le opzioni possibili per questo script.

E qui hai lo script di completamento automatico:

_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

Si noti che l'ultimo argomento da completare è il nome dello script a cui si desidera aggiungere il completamento automatico. Tutto quello che devi fare è aggiungere il tuo script di completamento automatico a bashrc come

source /path/to/your/autocomplete.sh

o copiarlo su /etc/bash.completion.d


1
A cosa serve la prevvariabile? Sembra che non lo usi.
dimo414,

@ dimo414 A quanto pare, ho rimosso quella variabile
terminando il

Cosa fa l' -o nospaceopzione?
Andrew Lamarra,

12

Se tutto ciò che desideri è un semplice completamento automatico basato su parole (quindi nessun completamento dei sottocomandi o altro), il completecomando ha -Wun'opzione che fa proprio la cosa giusta.

Ad esempio, ho le seguenti righe nel mio .bashrcper completare automaticamente un programma chiamato jupyter :

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

Ora si jupyter <TAB> <TAB>completa automaticamente per me.

I documenti su gnu.org sono utili.

Sembra fare affidamento sul fatto che la IFSvariabile sia impostata correttamente, ma ciò non ha causato problemi per me.

Per aggiungere il completamento del nome file e il completamento BASH predefinito, utilizzare l' -oopzione:

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

Per usarlo in zsh, aggiungi il seguente codice prima di eseguire il completecomando nel tuo ~/.zshrc:

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi

Come posso farlo funzionare con bash jupyter <TAB><TAB>?
papampi,

@papampi, penso che funzioni solo con un livello di completamento - penso che per farlo con 2 livelli avresti bisogno di una delle risposte più complicate sopra. Inoltre, di recente ho letto un tutorial abbastanza decente sul completamento della bash. Non fa esattamente ciò di cui hai bisogno, ma forse ti aiuterà. In bocca al lupo!
Ben
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.