complétion bash pour les modèles de fichiers ou les répertoires

12

J'essaie d'obtenir un script de complétion bash et j'ai des problèmes.

Je voudrais le configurer pour que les complétions répertoriées soient soit des fichiers correspondant à une extension particulière, soit des répertoires (qui peuvent ou non contenir des fichiers de cette extension).

Le problème que j'ai est que la seule façon d'obtenir des compléments pour contenir des fichiers et des répertoires est d'utiliser quelque chose comme -o plusdirs -f -X '!*.txt', mais quand je laisse bash terminer l'un des répertoires, cela ajoute juste un espace à la fin, plutôt qu'un sabrer.

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}
  local prev=${COMP_WORDS[COMP_CWORD-1]}

  #COMPREPLY=( $( compgen -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -f -G '*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o filenames -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o dirnames  -f -X '!*.txt' -- $cur ) )
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
  return 0
}

complete -F _xyz xyz

J'ai également essayé toutes les lignes commentées, mais elles ne développent même pas les répertoires.

Pour les tests, je l'ai exécuté dans un répertoire avec un fichier .txt et un répertoire "dir" (avec un fichier .txt à l'intérieur, bien que cela n'ait pas encore d'importance). Taper xyz <TAB>avec cette fonction répertorie le répertoire et le fichier .txt, mais la frappe se xyz d<TAB>développe en xyz dir(enfin, avec un espace après "dir").

Rob I
la source

Réponses:

10

Si vous regardez la fonction _cd()dans / etc / bash_completion , vous verrez que la fuite s'ajoute slash et complet est appelé avec l'option -o nospacepour cd .

Vous pouvez faire de même pour xyz , mais pour avoir à vérifier séparément si la correspondance trouvée est un répertoire (si c'est le cas, ajoutez une barre oblique) ou un fichier (si c'est le cas, ajoutez de l'espace). Cela devrait être fait dans une boucle for pour traiter toutes les correspondances trouvées.

En outre, pour gérer correctement les chemins d'accès qui contiennent des espaces, vous devez définir le séparateur de fichiers interne uniquement sur la nouvelle ligne et échapper les espaces. L'utilisation IFS=$'\n'en combinaison avec printf %qpermet à l'achèvement de fonctionner avec presque tous les personnages. 1 Des précautions particulières doivent être prises pour ne pas s'échapper de l'espace de fuite.

Les éléments suivants devraient fonctionner:

_xyz ()
{
    local IFS=$'\n'
    local LASTCHAR=' '

    COMPREPLY=($(compgen -o plusdirs -f -X '!*.txt' \
        -- "${COMP_WORDS[COMP_CWORD]}"))

    if [ ${#COMPREPLY[@]} = 1 ]; then
        [ -d "$COMPREPLY" ] && LASTCHAR=/
        COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR")
    else
        for ((i=0; i < ${#COMPREPLY[@]}; i++)); do
            [ -d "${COMPREPLY[$i]}" ] && COMPREPLY[$i]=${COMPREPLY[$i]}/
        done
    fi

    return 0
}

complete -o nospace -F _xyz xyz

1 Le caractère de nouvelle ligne est ici l'exception évidente, car il s'agit d'un séparateur de fichiers interne.

Dennis
la source
Cela fonctionne très bien (même si c'est dommage qu'il ne soit pas intégré). Merci!
Rob I
1
Une raison de ne pas utiliser "$ 2" au lieu de "$ {COMP_WORDS [COMP_CWORD]}"?
Edward Falk
3

Je pense que cette solution simple fonctionne en ce sens qu'elle:

  1. Correspond aux répertoires et fichiers se terminant par .txt
  2. Gère les espaces dans les noms de fichiers
  3. Ajoute une barre oblique à la fin des dossiers terminés sans espace de fin
  4. Ajoute de l'espace à la fin d'une correspondance d'achèvement de fichier

La clé passait -o filenamespour terminer. Cela a été testé sur GNU bash 3.2.25 sur RHEL 5.3 et GNU bash 4.3.18 sur osx

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}

  local IFS=$'\n'
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
}

complete -o filenames -F _xyz xyz
Chad Skeeters
la source
Oui, cela semble très bien fonctionner. Beaucoup plus simple, merci d'avoir saisi l'argument important!
Rob I