Achèvement de Bash pour les valeurs séparées par des virgules

16

Je voudrais créer une règle d'achèvement pour la liste des paramètres séparés par des virgules. Par exemple, j'ai une commande qui reçoit la liste des noms de serveurs:

myscript -s name1,name2,name3

En ce moment, j'ai réussi à écrire après l'achèvement:

_myscript () {
  local cur prev opts

  _get_comp_words_by_ref cur prev

  opts='-s'

  servers='name1 name2 name3'

  if [[ ${cur} == -* ]] ; then
    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
  else
    case "${prev}" in
      -s)
        if [[ "$cur" == *,* ]]; then
          local realcur prefix
          realcur=${cur##*,}
          prefix=${cur%,*}
          COMPREPLY=( $(compgen -W "${servers}" -P "${prefix}," -- ${realcur}) )
        else
          COMPREPLY=( $(compgen -W "${servers}" -- ${cur}) )
        fi
        ;;
      *)
        # do nothing
        ;;
    esac
  fi
}

Mais il a au moins 2 problèmes:

  1. Les suggestions de valeur actuelle incluent toutes les valeurs précédentes dans leur préfixe.
  2. Il ne prend pas en compte les valeurs en double.

Quelles sont les meilleures pratiques pour de tels cas? Peut-être que bash-complétions a des fonctions groupées pour les listes csv?

diffycat
la source
3
Ce qui pourrait aider, c'est que vous pouvez diviser les valeurs séparées par des virgules en une liste itérable comme celle-ci: IFS=, LIST=("$VARIABLE")où $ VARIABLE contient vos valeurs séparées par des virgules.
Michael Ehrenreich
2
Belle idée @MichaelEhrenreich, mais vous ne devez pas citer le $VARIABLE, sinon la rupture de mot ne se produit pas. il suffit d'utiliser IFS=, LIST=($VARIABLE).
Guss

Réponses:

6

Il n'y a fondamentalement aucun moyen de résoudre les problèmes que vous décrivez, car bash utilise les valeurs COMPREPLYdirectement dans l'affichage, puis pour remplacer le texte de l'utilisateur - tandis que pour obtenir ce que vous voulez, vous devez d'abord générer les complétions possibles (juste le supplément noms de serveur, sans le préfixe) pour bash à afficher, puis lorsque bash est sur le point de remplacer le texte de l'utilisateur par la plus longue chaîne non conflictuelle, vous en aurez besoin pour appeler à nouveau votre script pour générer le texte avec le préfixe - et bash n'a aucune facilité pour cela.

Le mieux que je pourrais trouver est de COMPREPLYgénérer le seul mot ayant le préfixe entier ( COMPREPLY=( "${prefix},"$(compgen -W "${servers[@]}" -- ${realcur}) )), de sorte que s'il n'y a qu'une seule complétion possible, il se termine automatiquement correctement, tandis que s'il y a plus d'une complétion possible , alors bash ne supprimera pas ce qui a été tapé jusqu'à présent (car le premier mot dans COMPREPLYa le préfixe entier et correspond donc au texte actuellement tapé et sera sélectionné par bash pour remplacer le texte de l'utilisateur) et affichera les options sans le préfixe - sauf pour ce seul mot qui contient déjà le préfixe, la sortie ressemblera à ceci:

$ command -s banana,a
ananas     apricot    banana,apple

"apple" comme trié en dernier dans les options de complétion car il porte le préfixe qui commence par "b" - très déroutant. Je ne recommande donc pas de faire cela.

En ce qui concerne les doublons - afin de ne pas afficher les doublons, il vous suffit de pénétrer $prefixdans sa partie (facile IFS="," prefix_parts=($prefix):) puis de les parcourir et de ne laisser que $serversles noms qui ne sont pas déjà répertoriés. Son fastidieux à taper, donc je ne le montrerai pas ici, mais relativement trivial donc je suis sûr que vous pouvez gérer :-).

Pour résumer, je ne pense pas que vous devriez utiliser des valeurs séparées par des virgules pour les options d'entrée, du moins si vous vous attendez à ce que bash vous aide à terminer.

Vous pouvez prendre en charge un format d'options qui ressemble à ceci: command -s <server> [<server> [..]]puis pour terminer les entrées autres que celle immédiatement après l' -soption, parcourez simplement le $COMP_WORDStableau $COMP_CWORDjusqu'à ce que vous trouviez une option (chaîne qui correspond -*) et si ses "-s" puis vous devez compléter le nom du serveur.

Guss
la source