¿Cómo crear script con autocompletar?

96

Cuando uso un programa como svn y escribo en Gnome Terminal:

svn upd

y presionar Tab se autocompleta a:

svn update

¿Es posible hacer algo así en mi script bash personalizado?

    
pregunta UAdapter 17.10.2011 - 17:12

5 respuestas

33

Puede usar la finalización programable . Veamos /etc/bash_completion y /etc/bash_completion.d/* para algunos ejemplos.

    
respondido por el Florian Diesch 17.10.2011 - 17:54
160

Tengo seis meses de retraso pero estaba buscando lo mismo y encontré esto:

Tendrás que crear un nuevo archivo:

/etc/bash_completion.d/foo

Para un autocompletado estático ( --help / --verbose por ejemplo) agregue esto:

_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 es una matriz que contiene todas las palabras individuales en la línea de comando actual.
  • COMP_CWORD es un índice de la palabra que contiene la posición actual del cursor.
  • COMPREPLY es una variable de matriz desde la cual Bash lee las posibles terminaciones.

Y el comando compgen devuelve la matriz de elementos de --help , --verbose y --version que coincide con la palabra actual "${cur}" :

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

Fuente: enlace

    
respondido por el Louis Soulez 13.09.2013 - 17:41
33

Todas las terminaciones de bash se almacenan en /etc/bash_completion.d/ . Por lo tanto, si está creando un software con bash_completion, valdría la pena hacer que la instalación de deb / make incluya un archivo con el nombre del software en ese directorio. Aquí hay un ejemplo de script de finalización de bash para 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

Probablemente valga la pena revisar uno de los archivos de finalización de bash que más se ajusta a su programa. Uno de los ejemplos más simples es el archivo rrdtool .

    
respondido por el Marco Ceppi 13.01.2012 - 18:28
28

Aquí hay un tutorial completo.

Tengamos un ejemplo de script llamado admin.sh al que le gustaría que el autocompletado funcione.

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

Tenga en cuenta la lista de opciones. La secuencia de comandos de llamada con esta opción imprimirá todas las opciones posibles para esta secuencia de comandos.

Y aquí tienes el script de autocompletar:

_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

Tenga en cuenta que el último argumento para completar es el nombre del guión al que desea agregar autocompletado. Todo lo que necesita hacer es agregar su script de autocompletar a bashrc como

source /path/to/your/autocomplete.sh

o cópialo en     /etc/bash.completion.d

    
respondido por el kokosing 14.06.2014 - 12:33
7

Si lo único que quieres es una simple autocompletación basada en palabras (para que no haya finalización del subcomando ni nada), el comando complete tiene una opción -W que hace lo correcto.

Por ejemplo, tengo las siguientes líneas en mi .bashrc para autocompletar un programa llamado jupyter :

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

Ahora jupyter <TAB> <TAB> se autocompleta para mí.

Los documentos en gnu.org son útiles.

Parece depender de que la variable IFS esté configurada correctamente, pero eso no me ha causado problemas.

Para agregar la finalización del nombre de archivo y la finalización BASH predeterminada, use la opción -o :

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

Para usar esto en zsh, agregue el siguiente código antes de ejecutar el comando complete en su ~/.zshrc :

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

Lea otras preguntas en las etiquetas