Une confusion à propos de $ {array } versus $ {array } dans le contexte d’une complétion Bash

Je suis en train d’écrire un achèvement de Bash pour la première fois, et je suis un peu confus à propos des deux manières de déréférencer les tableaux de Bash ( ${array[@]} et ${array[*]} ).

Voici le morceau de code pertinent (ça marche, au fait, mais je voudrais 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} ) ) } 

La documentation de Bash dit :

Tout élément d’un tableau peut être référencé à l’aide de $ {name [subscript]}. Les accolades sont nécessaires pour éviter les conflits avec les opérateurs d’extension de nom de fichier du shell. Si l’indice est ‘@’ ou ‘*’, le mot s’étend à tous les membres du nom du tableau. Ces indices ne diffèrent que lorsque le mot apparaît entre guillemets. Si le mot est entre guillemets, $ {name [*]} 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 $ {name [@]} développe chaque élément de nom. à un mot séparé.

Maintenant, je crois comprendre que compgen -W attend une chaîne contenant une liste de mots d’alternatives possibles, mais dans ce contexte, je ne comprends pas ce que “$ {name [@]} développe chaque élément de nom en un mot séparé”.

Longue histoire courte: ${array[*]} fonctionne; ${array[@]} ne le fait pas. Je voudrais savoir pourquoi et j’aimerais mieux comprendre exactement ce que ${array[@]} développe.

(Ceci est une extension de mon commentaire sur la réponse de Kaleb Pederson – voyez cette réponse pour un traitement plus général de [@] vs [*] .)

Lorsque bash (ou tout autre shell similaire) parsing une ligne de commande, il le divise en une série de “mots” (que j’appellerai “shell-words” pour éviter toute confusion ultérieure). Généralement, les mots-shell sont séparés par des espaces (ou d’autres espaces), mais les espaces peuvent être inclus dans un mot-shell en les échappant ou en les citant. La différence entre les tableaux étendus de [@] et [*] entre guillemets est que "${myarray[@]}" conduit à ce que chaque élément du tableau soit traité comme un mot-shell distinct, tandis que "${myarray[*]}" résulte en un seul mot-shell avec tous les éléments du tableau séparés par des espaces (ou quel que soit le premier caractère de IFS ).

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

Fournir une liste de mots d’achèvement (que j’appellerai des mots-comp pour éviter la confusion avec les mots-shell) à compgen est différent; L’option -W prend une liste de mots-clés, mais elle doit être sous la forme d’un mot-shell unique avec les mots-clés séparés par des espaces. Notez que les options de commande qui prennent toujours des arguments (du moins pour autant que je sache) prennent un seul mot-clé – sinon il n’y aurait aucun moyen de dire quand les arguments de l’option se termineraient et les arguments de la commande options drapeaux) commencent.

Plus en détail:

 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 un non-sens complet: “perl-one” est le seul mot de passe attaché à l’option -W, et le premier argument réel – que compgen prendra comme chaîne à compléter – est “perl-two / usr / bin / perl “. Je m’attendrais à ce que compgen se plaint d’avoir reçu des arguments supplémentaires (“-” et tout ce qui se trouve dans $ cur), mais apparemment, il ne fait que les ignorer.

Votre titre parle de ${array[@]} versus ${array[*]} mais vous vous posez des questions sur $array[*] versus $array[@] ce qui est un peu déroutant. Je vais répondre à la fois:

Lorsque vous citez une variable de tableau et que vous utilisez @ comme indice, chaque élément du tableau est étendu à son contenu complet, quel que soit l’espace (en fait, l’un des $IFS ) pouvant être présent dans ce contenu. Lorsque vous utilisez l’astérisque ( * ) comme indice (qu’il soit ou non cité), il peut devenir un nouveau contenu créé en divisant le contenu de chaque élément du tableau à $IFS .

Voici l’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 sa sortie:

 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' 

"${myarray[@]}" je veux habituellement "${myarray[@]}" . Maintenant, pour répondre à la deuxième partie de votre question, ${array[@]} versus $array[@] .

Citant les documents bash, que vous avez cités:

Les accolades sont nécessaires pour éviter les conflits avec les opérateurs d’extension de nom de fichier du shell.

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

Mais quand vous faites $myarray[@] , le signe dollar est étroitement lié à myarray , il est donc évalué avant le [@] . Par exemple:

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

Mais, comme indiqué dans la documentation, les parenthèses sont destinées à l’extension du nom de fichier. Essayons ceci:

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

Maintenant, nous pouvons voir que l’extension du nom de fichier est survenue après l’expansion de $myarray myarray.

Et une autre remarque, $myarray sans indice augmente à la première valeur du tableau:

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