J'avais l'habitude d'être confiant sur le fait que les guillemets sont toujours une bonne pratique afin d'éviter que le shell ne les analyse.
Puis je suis tombé sur ceci:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Essayer d'isoler le problème, obtenir la même erreur:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
J'ai résolu le problème comme ceci:
[ "$x" = '1' ] && [ "$y" = '1' ]
J'ai encore besoin de savoir ce qui se passe ici.
[[ "$x" = '1' && "$y" = '1' ]]
-a
et-o
comme obsolète pour cette raison (c'est ce que signifie l'[OB]
exposant à côté de leur spécification). Si vous écriviez à la[ "$x" = 1 ] && [ "$y" = 1 ]
place, vous iriez bien et vous seriez bien dans le domaine du comportement bien défini / standardisé.[ "x$x" = "x1" ]
éviter que les arguments soient mal interprétés en tant qu'opérateurs.dash
plutôt que Bash, c'est toujours une pratique utile.Réponses:
Il s'agit d'un cas d'angle très obscur que l'on pourrait considérer comme un bug dans la façon dont le test
[
intégré est défini; cependant, il correspond au comportement du[
binaire réel disponible sur de nombreux systèmes. Pour autant que je peux dire, il ne touche que certains cas et une variable ayant une valeur qui correspond à un[
opérateur comme(
,!
,=
,-e
et ainsi de suite.Permettez-moi d'expliquer pourquoi et comment contourner ce problème dans les shells Bash et POSIX.
Explication:
Considérer ce qui suit:
Aucun problème; ce qui précède ne produit aucune erreur et produit
yes
. C'est ainsi que nous nous attendons à ce que les choses fonctionnent. Vous pouvez changer la chaîne de comparaison'1'
si vous le souhaitez, et la valeur dex
, et cela fonctionnera comme prévu.Notez que le
/usr/bin/[
binaire réel se comporte de la même manière. Si vous exécutez, par exemple,'/usr/bin/[' '(' = '(' ']'
il n'y a pas d'erreur, car le programme peut détecter que les arguments consistent en une opération de comparaison de chaîne unique.Le bogue se produit lorsque nous et avec une deuxième expression. Peu importe la deuxième expression, tant qu'elle est valide. Par exemple,
sorties
yes
, et est évidemment une expression valide; mais, si nous combinons les deux,Bash rejette l'expression si et seulement si
x
est(
ou!
.Si nous devions exécuter ce qui précède en utilisant le
[
programme réel , à savoirl'erreur serait compréhensible: puisque le shell effectue les substitutions de variables, le
/usr/bin/[
binaire ne reçoit que les paramètres(
=
(
-a
1
=
1
et la terminaison]
, il échoue naturellement à analyser si les parenthèses ouvertes commencent ou non une sous-expression, une opération et une opération étant impliquées. Bien sûr, l'analyser comme deux comparaisons de chaînes est possible, mais le faire avec avidité comme cela pourrait causer des problèmes lorsqu'il est appliqué à des expressions appropriées avec des sous-expressions entre parenthèses.Le problème, en fait, est que le shell
[
intégré se comporte de la même manière, comme s'il développait la valeur dex
avant d'examiner l'expression.(Ces ambiguïtés, et d'autres liées à l'expansion des variables, étaient une grande raison pour laquelle Bash a implémenté et recommande désormais d'utiliser les
[[ ... ]]
expressions de test à la place.)La solution de contournement est triviale et souvent vue dans les scripts utilisant des
sh
shells plus anciens . Vous ajoutez souvent un caractère "sûr"x
devant les chaînes (les deux valeurs étant comparées), pour vous assurer que l'expression est reconnue comme une comparaison de chaînes:la source
[
bogue intégré. Si quelque chose, c'est un défaut de conception inhérent.[[
est un mot - clé shell , pas seulement une commande intégrée, il permet donc de regarder les choses avant la suppression des guillemets et de remplacer le fractionnement de mots habituel. par exemple,[[ $x == 1 ]]
n'a pas besoin d'être utilisé"$x"
, car un[[
contexte est différent de la normale. Quoi qu'il en soit, voilà comment[[
est en mesure d'éviter les pièges de[
. POSIX nécessite[
de se comporter comme il le fait, et bash est principalement compatible POSIX même sans--posix
, donc le changement[
en mot-clé n'est pas attrayant.[
.[
le comportement étant gêné par l'histoire antérieure à POSIX, j'ai plutôt choisi ce dernier mot.) J'évite personnellement d'utiliser[[
dans mes exemples de scripts, mais uniquement parce que c'est un exception à la recommandation de citation que je harpe toujours (car l'omission de guillemets est la raison la plus courante des bogues de script que je vois), et je n'ai pas pensé à un paragraphe simple pour expliquer pourquoi[[
une exception aux règles, sans faire ma recommandation de citation suspect.sh
scripts, bien antérieure à la standardisation POSIX. Utiliser des sous-expressions entre parenthèses-a
et-o
des opérateurs logiques dans les tests /[
est plus efficace que de s'appuyer sur le chaînage d'expression (via&&
et||
); c'est juste que sur les machines actuelles, la différence n'est pas pertinente.&&
et||
, mais la raison en est que cela facilite la tâche de nous les humains à maintenir (lire, comprendre et modifier si / quand nécessaire) sans introduire de bogues. Donc, je ne critique pas votre suggestion, mais seulement le raisonnement qui la sous-tend. (Pour des raisons très similaires, la.LT.
,.GE.
, etc. opérateurs de comparaison dans FORTRAN 77 a obtenu beaucoup plus versions compréhensibles par l' homme<
,>=
etc. dans les versions ultérieures.)[
akatest
voit:test
accepte les sous-expressions entre parenthèses; il pense donc que la parenthèse gauche ouvre une sous-expression et essaie de l'analyser; l'analyseur voit=
comme la première chose dans la sous-expression et pense que c'est un test implicite de longueur de chaîne, donc il est content; la sous-expression doit ensuite être suivie d'une parenthèse droite, et à la place l'analyseur trouve à la1
place de)
. Et ça se plaint.Lorsqu'il
test
a exactement trois arguments et que l'argument du milieu est l'un des opérateurs reconnus, il applique cet opérateur aux 1er et 3e arguments sans rechercher de sous-expressions entre parenthèses.Pour les détails complets
man bash
, rechercheztest expr
.Conclusion: l'algorithme d'analyse utilisé par
test
est compliqué. Utilisez uniquement des expressions simples et utilisez les opérateurs shell!
,&&
et||
pour les combiner.la source