118 votes

Confusion entre ${array[*]} et ${array[@]} dans le contexte d'une complétion bash

Je m'essaye pour la première fois à l'écriture d'une complétion en bash, et je suis un peu confus à propos des deux façons de déréférencer les tableaux en bash ( ${array[@]} y ${array[*]} ).

Voici le morceau de code correspondant (il fonctionne, mais j'aimerais mieux le comprendre) :

_switch()
{
    local cur perls
    local ROOT=${PERLBREW_ROOT:-$HOME/perl5/perlbrew}
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    perls=($ROOT/perls/perl-*)
    # remove all but the final part of the name
    perls=(${perls[*]##*/})

    COMPREPLY=( $( compgen -W "${perls[*]} /usr/bin/perl" -- ${cur} ) )
}

de bash la documentation dit :

Tout élément d'un tableau peut être référencé en utilisant ${nom[indice]}. Les accolades sont nécessaires pour éviter les conflits avec les opérateurs d'expansion de nom de fichier de l'interpréteur de commandes. Si l'indice est "@" ou "*", le mot est étendu à tous les membres du nom du tableau. Ces indices ne diffèrent que lorsque le mot apparaît entre guillemets doubles. Si le mot est entre guillemets doubles, ${nom[*]} se développe en un seul mot avec la valeur de chaque membre du tableau séparée par le premier caractère de la variable IFS, et ${nom[@]} développe chaque élément de nom en un mot distinct.

Je crois que je comprends maintenant que compgen -W attend une chaîne contenant une liste de mots possibles, mais dans ce contexte, je ne comprends pas ce que signifie "${nom[@]} développe chaque élément de nom en un mot distinct".

Pour faire court : ${array[*]} œuvre ; ${array[@]} ne le fait pas. J'aimerais savoir pourquoi, et je voudrais mieux comprendre ce qu'est exactement l'action de l'Union européenne. ${array[@]} se développe en.

159voto

Gordon Davisson Points 22534

(Il s'agit d'un développement de mon commentaire sur la réponse de Kaleb Pederson - voir cette réponse pour un traitement plus général de la question des [@] vs [*] .)

Lorsque bash (ou tout autre shell similaire) analyse une ligne de commande, il la divise en une série de "mots" (que j'appellerai "mots shell" pour éviter toute confusion ultérieure). En général, les mots de l'interpréteur de commandes sont séparés par des espaces (ou d'autres espaces blancs), mais les espaces peuvent être inclus dans un mot de l'interpréteur de commandes en les échappant ou en les mettant entre guillemets. La différence entre [@] y [*] -Les tableaux étendus entre guillemets sont les suivants "${myarray[@]}" conduit à traiter chaque élément du tableau comme un mot-objet distinct, tandis que "${myarray[*]}" résulte en un seul mot-objet dont tous les éléments du tableau sont séparés par des espaces (ou par le premier caractère de IFS est).

En général, le [@] est ce que vous voulez. Supposons que nous ayons perls=(perl-one perl-two) et utiliser ls "${perls[*]}" -- ce qui équivaut à ls "perl-one perl-two" qui recherchera un seul fichier nommé perl-one perl-two ce qui n'est probablement pas ce que vous vouliez. ls "${perls[@]}" est équivalent à ls "perl-one" "perl-two" qui est beaucoup plus susceptible de faire quelque chose d'utile.

Fournir une liste de mots d'achèvement (que j'appellerai mots d'achèvement pour éviter toute confusion avec les mots d'achèvement) à compgen est différent ; le -W prend en charge une liste de mots-objets, mais celle-ci doit se présenter sous la forme d'un mot-objet unique, les mots-objets étant séparés par des espaces. Notez que les options de commande qui prennent des arguments prennent toujours (du moins à ma connaissance) un seul mot-objet -- sinon il n'y aurait aucun moyen de savoir quand les arguments de l'option se terminent, et quand les arguments normaux de la commande (/autres drapeaux d'option) commencent.

Plus de détails :

perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}

est équivalent à :

compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}

...qui fait ce que vous voulez. D'autre part,

perls=(perl-one perl-two)
compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}

est équivalent à :

compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}

...ce qui est complètement absurde : "perl-one" est le seul mot comp attaché à l'option -W, et le premier vrai argument -- que compgen prendra comme la chaîne à compléter -- est "perl-two /usr/bin/perl". Je m'attendrais à ce que compgen se plaigne d'avoir reçu des arguments supplémentaires ("--" et tout ce qu'il y a dans $cur), mais apparemment il les ignore.

84voto

Kaleb Pederson Points 22428

Votre titre pose des questions sur ${array[@]} contre ${array[*]} (tant à l'intérieur qu'à l'extérieur de l'Union européenne). {} ), mais vous posez ensuite des questions sur $array[*] contre $array[@] (tous deux sans {} ), ce qui est un peu déroutant. Je répondrai aux deux (dans les limites de {} ):

Lorsque vous citez une variable de type tableau et que vous utilisez @ en tant qu'indice, chaque élément du tableau est étendu à son contenu complet sans tenir compte des espaces (en fait, l'un des éléments de l'indice $IFS ) qui peuvent être présents dans ce contenu. Lorsque vous utilisez l'astérisque ( * ) en tant qu'indice (qu'il soit cité ou non), il peut s'étendre à un nouveau contenu créé en décomposant le contenu de chaque élément du tableau à l'adresse $IFS .

Voici un exemple de script :

#!/bin/sh

myarray[0]="one"
myarray[1]="two"
myarray[3]="three four"

echo "with quotes around myarray[*]"
for x in "${myarray[*]}"; do
        echo "ARG[*]: '$x'"
done

echo "with quotes around myarray[@]"
for x in "${myarray[@]}"; do
        echo "ARG[@]: '$x'"
done

echo "without quotes around myarray[*]"
for x in ${myarray[*]}; do
        echo "ARG[*]: '$x'"
done

echo "without quotes around myarray[@]"
for x in ${myarray[@]}; do
        echo "ARG[@]: '$x'"
done

Et voici le résultat :

with quotes around myarray[*]
ARG[*]: 'one two three four'
with quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three four'
without quotes around myarray[*]
ARG[*]: 'one'
ARG[*]: 'two'
ARG[*]: 'three'
ARG[*]: 'four'
without quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three'
ARG[@]: 'four'

Personnellement, je souhaite généralement "${myarray[@]}" . Maintenant, pour répondre à la deuxième partie de votre question, ${array[@]} contre $array[@] .

Je cite la documentation de bash, que vous avez citée :

Les accolades sont nécessaires pour éviter les conflits avec les opérateurs d'expansion de noms de fichiers de l'interpréteur de commandes.

$ myarray=
$ myarray[0]="one"
$ myarray[1]="two"
$ echo ${myarray[@]}
one two

Mais lorsque vous le faites $myarray[@] le signe du dollar est étroitement lié à myarray de sorte qu'il est évalué avant le [@] . Par exemple :

$ ls $myarray[@]
ls: cannot access one[@]: No such file or directory

Mais, comme indiqué dans la documentation, les crochets servent à l'expansion du nom de fichier, alors essayons ceci :

$ touch one@
$ ls $myarray[@]
one@

Nous pouvons maintenant voir que l'expansion du nom de fichier s'est produite après la commande $myarray l'exapansion.

Une dernière remarque, $myarray sans indice se développe jusqu'à la première valeur du tableau :

$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X