13 votes

complétion bash pour les noms de fichiers ou de répertoires

J'essaie de mettre en place un script de complétion bash script et j'ai quelques difficultés.

J'aimerais faire en sorte que les compléments listés 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 je rencontre est que la seule façon de faire en sorte que les complétions contiennent des fichiers et est d'utiliser quelque chose comme -o plusdirs -f -X '!*.txt' mais lorsque je laisse bash compléter l'un des répertoires, il ajoute simplement un espace à la fin, au lieu d'une barre oblique.

_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 essayé toutes les lignes commentées, mais elles ne développent même pas les répertoires.

Pour les tests, je l'ai lancé dans un répertoire contenant un fichier .txt et un répertoire "dir" (avec un fichier .txt à l'intérieur, mais cela n'a pas encore d'importance). Tapez xyz <TAB> avec cette fonction permet d'obtenir la liste du répertoire et du fichier .txt, mais en tapant xyz d<TAB> s'étend à xyz dir (avec un espace après "dir").

10voto

Dennis Points 46916

Si vous regardez la fonction _cd() en /etc/bash_completion vous verrez qu'il ajoute lui-même la barre oblique de fin de ligne et que complet est appelé avec l'option -o nospace para cd .

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

En outre, pour gérer correctement les chemins contenant des espaces, vous devez définir le séparateur de fichier interne comme étant uniquement une nouvelle ligne et ignorer les espaces. L'utilisation de IFS=$'\n' en combinaison avec printf %q permet à l'achèvement de fonctionner avec presque tous les personnages. 1 Il convient de veiller tout particulièrement à ce que l'espace de fin ne soit pas oublié.

Ce qui suit devrait 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

<sup>1 </sup>Le caractère de retour à la ligne est l'exception évidente, puisqu'il s'agit d'un séparateur de fichiers interne.

3voto

Chad Skeeters Points 303

Je pense que cette solution simple fonctionne en ce sens :

  1. Recherche les répertoires et les fichiers qui se terminent par .txt
  2. Gestion des espaces dans les noms de fichiers
  3. Ajoute une barre oblique à la fin des compléments de dossier sans espace à la fin.
  4. Ajoute de l'espace à la fin d'un fichier pour compléter la correspondance.

La clé était de passer -o filenames à compléter. Ceci 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

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X