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 (voirset -- "$@";echo $#
conset -- "$*";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