13 votes

Pourquoi les tableaux vides sont traités comme unset en bash ?

Récemment, j'ai configuré le programme de Microsoft Sous-système Windows pour Linux sur mon ordinateur. Il émule simplement un environnement Linux et d'autres choses ; en gros, c'est Cygwin, mais un peu mieux connecté au système Windows sous-jacent. Après être passé de Cygwin à WSL, cependant, j'ai rencontré un problème. Je ne sais pas si c'est particulier à l'implémentation de Windows ou non, mais cela ne se produit pas dans Cygwin.

Pour attraper les bogues de mon code un peu plus rapidement, j'ai pris l'habitude d'utiliser la fonction de bash set -u qui permet à l'interpréteur de commandes de "traiter les variables non définies comme une erreur lors de la substitution". Sans cette option, bash traite les variables unset comme des variables définies sur la chaîne vide lorsqu'il les développe.

Cependant, cela a une étrange conséquence inattendue (du moins sur WSL) en ce qui concerne les tableaux :

Me@Computer:~$ set -u
==>
Me@Computer:~$ declare -p array
==> bash: declare: array: not found
Me@Computer:~$ array=( )
==>
Me@Computer:~$ declare -p array
==> declare -a array='()'
Me@Computer:~$ echo "${array[@]}"       # Expands to "echo" (with 0 args), right?
==> bash: array[@]: unbound variable    # Wrong! wtf, bash??

Comme vous pouvez le voir dans la sortie de declare -p array , bash fait reconnaît la différence entre un tableau vide et un tableau non défini - jusqu'à ce qu'il soit temps de l'étendre, après quoi bash fait une crise. Je sais que bash traite l'élément @ y * variables spécialement, et encore plus lorsqu'elles sont citées, alors j'ai essayé un tas de trucs. Rien ne marche :

Me@Computer:~$ echo "${array[@]}"
==> bash: array[@]: unbound variable
Me@Computer:~$ echo "${array[*]}"
==> bash: array[*]: unbound variable
Me@Computer:~$ echo ${array[@]}
==> bash: array[@]: unbound variable
Me@Computer:~$ echo ${array[*]}
==> bash: array[*]: unbound variable

Assez bizarrement, je peut accéder au tableau d'indices du tableau ; cependant, bash rencontre alors le problème inverse en ce qu'il también réussit lorsqu'on lui demande les indices d'un tableau non défini :

Me@Computer:~$ echo "${!array[@]}"
==>
Me@Computer:~$ echo "${!unset_array[@]}"
==>

(Ce qui précède fonctionne pour toutes les variations des formats d'expansion de tableau).

Le plus frustrant est que je ne peux même pas accéder à la longueur d'un tableau vide :

Me@Computer:~$ echo "${#array[@]}"
==> bash: array[@]: unbound variable

Cela aussi échoue avec toutes les variations du format.

Quelqu'un sait-il pourquoi cela se produit ? S'agit-il d'un bogue ou d'un comportement attendu ? Dans ce dernier cas, quelle est la motivation ? Existe-t-il un moyen de désactiver ce comportement qui me permette de garder le contrôle de l'ordinateur ? set -u ?


Solution(s) :

J'ai trouvé un moyen de contourner le problème en tirant parti du fait que les paramètres de position ne sont pas affectés par ce phénomène. Si quelqu'un en trouve une meilleure, faites-le moi savoir !

Me@Computer:~$ tmp=( "$@" )                    # Stash the real positional params; we need that array
Me@Computer:~$ set --                          # "$@" is now empty.
Me@Computer:~$ example_cmd "${array[@]-$@}"    # Now expands w/out error *and* w/ the right number of args
Me@Computer:~$ set -- "${tmp-$@}"              # Put the positional params back where we found them
Me@Computer:~$ unset tmp                       # Cleaning up after ourselves

(Notez que vous devez toujours utiliser une astuce lors de la réinitialisation des paramètres de position, juste au cas où ils étaient eux-mêmes vides à l'origine). Ces contorsions devraient être effectuées à chaque fois qu'un tableau potentiellement vide est utilisé.


Autres notes :

  • test -v pense également que les tableaux vides ne sont pas mis à jour, contrairement à declare -p .
  • Les mêmes problèmes se posent avec les tableaux associatifs.
  • J'ai essayé d'initialiser le tableau avec declare (c'est-à-dire, declare -a array=( ) ), mais cela n'a rien changé.
  • Les tableaux de paramètres positionnels, heureusement, semblent être à l'abri de ce phénomène.
  • Je pensais simplement utiliser "${array[@]-}" chaque fois que je voulais accéder à un tableau, mais cela ne fonctionnera pas dans tous les scénarios. "${array[@]}" lorsqu'il est cité deux fois, est censé se développer en mots séparés pour chaque élément du tableau ; un tableau vide devrait donc être développé en 0 mot (voir set -- "$@";echo $# con set -- "$*";echo $# ). "${array[@]-}" cependant, se développe en un seul mot, la chaîne vide.

Informations sur la version et l'environnement :

Comme je l'ai dit en haut, j'utilise le sous-système Windows pour Linux sur Windows 10. Autres informations :

Me@Computer:~$ bash --version
==> GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
    ...
Me@Computer:~$ echo "$-"
==> himuBCH

6voto

Benjamin W. Points 19035

Cela n'est pas spécifique à Bash fonctionnant sous WSL ou non, mais dépend de la version de Bash.

Ce comportement a été signalé comme un bogue pour Bash 4.1, mais était comportement envisagé . Chet fait également remarquer que le comportement différent pour les $@ y $* c'est parce que POSIX l'exige. La solution de contournement recommandée à l'époque, similaire au commentaire d'Andy, était la suivante :

echo ${argv[0]+"${argv[@]}"}

qui s'étend à "${argv[@]}" si argv est définie, et rien sinon (remarquez que l'expansion externe n'est pas citée).

Dans Bash 4.4, le comportement a changé, comme l'indique le document CHANGEMENTS de bash-4.4-beta2 à bash-4.4-rc2, comme une "nouvelle fonctionnalité" :

Utilisation de ${a[@]} o ${a[*]} avec un tableau sans éléments assignés lorsque le nounset est activée ne provoque plus d'erreur liée à une variable non liée.

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