Come creare script con completamento automatico?

95

Quando uso un programma come svn e scrivo Gnome Terminal:

svn upd

e premi Tab viene completato automaticamente per:

svn update

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

    
posta UAdapter 17.10.2011 - 17:12

5 risposte

33

Puoi utilizzare il Completamento programmabile . Dai un'occhiata a /etc/bash_completion e /etc/bash_completion.d/* per alcuni esempi.

    
risposta data Florian Diesch 17.10.2011 - 17:54
159

Ho sei mesi di ritardo 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 / --verbose per 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 che contiene la posizione corrente del cursore.
  • COMPREPLY è una variabile di matrice da cui Bash legge i possibili completamenti.

E il comando compgen restituisce la matrice di elementi da --help , --verbose e --version che corrispondono alla parola corrente "${cur}" :

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

Fonte: link

    
risposta data Louis Soulez 13.09.2013 - 17:41
33

Tutti i completamenti bash sono memorizzati in /etc/bash_completion.d/ . Quindi, se stai costruendo software con bash_completion, varrebbe la pena fare in modo che deb / make install lasci un file con il nome del software in quella directory. Ecco uno script di completamento di bash di esempio 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
    [email protected](config|password-file|include-from|exclude-from))
        _filedir
        return 0
        ;;
    [email protected](T|-temp-dir|-compare-dest))
        _filedir -d
        return 0
        ;;
    [email protected](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]}" == [email protected](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

Probabilmente varrebbe la pena di rivedere uno dei file di completamento di bash che corrispondono maggiormente al tuo programma. Uno degli esempi più semplici è il file rrdtool .

    
risposta data Marco Ceppi 13.01.2012 - 18:28
28

Ecco un tutorial completo.

Abbiamo un esempio di script chiamato admin.sh a cui vorresti avere il completamento automatico.

#!/bin/bash

while [ $# -gt 0 ]; do
  arg=
  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

Nota lista di opzioni. Lo script di chiamata 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 autocomplete a bashrc come

source /path/to/your/autocomplete.sh

o copialo in     /etc/bash.completion.d

    
risposta data kokosing 14.06.2014 - 12:33
7

Se tutto ciò che si desidera è un semplice completamento automatico basato su parole (quindi nessun completamento del sottocomando o altro), il comando complete ha un'opzione -W che fa la cosa giusta.

Ad esempio, ho le seguenti righe nel mio .bashrc per 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 jupyter <TAB> <TAB> autocompleta per me.

I documenti su gnu.org sono utili.

Sembra che la variabile IFS sia impostata correttamente, ma ciò non ha causato alcun problema per me.

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

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

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

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi
    
risposta data Ben 28.11.2016 - 01:04

Leggi altre domande sui tag