web-dev-qa-db-fra.com

Comment créer un script avec l'auto-complétion?

Quand j'utilise un programme comme svn et que je tape dans Gnome Terminal:

svn upd

et frapper Tab c'est auto-complété à:

svn update

Est-il possible de faire quelque chose comme ça dans mon script bash personnalisé?

116
UAdapter

Vous pouvez utiliser le Fin programmable . Regardez /etc/bash_completion et /etc/bash_completion.d/* pour quelques exemples.

42
Florian Diesch

J'ai six mois de retard mais je cherchais la même chose et j'ai trouvé ceci:

Vous devrez créer un nouveau fichier:

/etc/bash_completion.d/foo

Pour une complétion automatique statique (--help/--verbose par exemple), ajoutez ceci:

_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 est un tableau contenant tous les mots de la ligne de commande actuelle.
  • COMP_CWORD est un index du mot contenant la position actuelle du curseur.
  • COMPREPLY est une variable de tableau à partir de laquelle Bash lit les complétions possibles.

Et la commande compgen renvoie le tableau des éléments de --help, --verbose et --version correspondant au mot Word "${cur}" actuel:

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

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

197
Louis Soulez

Toutes les finitions de bash sont stockées dans /etc/bash_completion.d/. Donc, si vous construisez un logiciel avec bash_completion, il serait intéressant de demander à deb/make install de déposer un fichier avec le nom du logiciel dans ce répertoire. Voici un exemple de script d'achèvement de bash pour 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

Il serait probablement intéressant d’examiner l’un des fichiers de fin d’essais correspondant à votre programme. L'un des exemples les plus simples est le fichier rrdtool.

34
Marco Ceppi

Voici un tutoriel complet.

Donnons un exemple de script appelé admin.sh auquel vous souhaiteriez que la saisie semi-automatique fonctionne.

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

Notez la liste des options. Appeler un script avec cette option affichera toutes les options possibles pour ce script.

Et ici vous avez le script autocomplete:

_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

Notez que le dernier argument à compléter est le nom du script auquel vous souhaitez ajouter la complétion automatique. Tout ce que vous avez à faire est d’ajouter votre script autocomplete à bashrc as

source /path/to/your/autocomplete.sh

ou le copier dans /etc/bash.completion.d

32
kokosing

Si tout ce que vous voulez, c'est une simple complétion automatique basée sur Word (donc aucune complétion de sous-commande ou quoi que ce soit), la commande complete a une option -W qui fait juste ce qu'il faut.

Par exemple, mon .bashrc contient les lignes suivantes pour compléter automatiquement un programme appelé jupyter :

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

jupyter <TAB> <TAB> s'auto-complète maintenant.

Les docs à gnu.org sont utiles.

Il semble que la variable IFS soit correctement définie, mais cela ne m'a posé aucun problème.

Pour ajouter l'achèvement du nom de fichier et l'achèvement de BASH par défaut, utilisez l'option -o:

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

Pour utiliser cela dans zsh, ajoutez le code suivant avant d'exécuter la commande complete dans votre ~/.zshrc:

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