Pourquoi [-n] n'est-il pas faux comme [-n “”]?

20

Ma question porte sur les valeurs de retour produites par ce code:

if [ -n ]; then echo "true"; else echo "false"; fi

Cela imprime true.

Son test complémentaire utilisant [ -z ]imprime également true:

if [ -z ]; then echo "true"; else  echo "false"; fi

Dans le code ci-dessus, pourquoi le [ -n ]test suppose-t-il que la valeur de chaîne qui n'est pas du tout passée, comme non nulle?

Le code ci-dessous s'imprime false. Cela est attendu car la valeur de chaîne transmise est nulle et de longueur nulle.

if [ -n "" ]; then echo "true"; else  echo "false"; fi
Ravi Kumar
la source

Réponses:

14

[x] est équivalent à [ -nx] même si x commence par à -condition qu'il n'y ait pas d'opérande.

$ [ -o ] ; echo $?
0
$ [ -eq ] ; echo $?
0
$ [ -n -o ] ; echo $?
0
$ [ -n -eq ] ; echo $?
0
Ignacio Vazquez-Abrams
la source
4
Notez que, historiquement, [ -t ]testait si stdout était un terminal (abréviation de [ -t 1 ]) et que certains shells le font toujours (dans le cas ksh93où cela -tn'est que littéral), il est donc préférable d'utiliser [ -n "$var" ]que [ "$var" ]. Bien que cela échouerait toujours dans certaines anciennes testimplémentations pour des valeurs $varsimilaires =, auquel cas [ "" != "$var" ]ou [ "x$var" != x ]ou case $x in "")...peut-être mieux.
Stéphane Chazelas
3
Choix intéressant d' -oici. Dans certains shells comme bash, -oest à la fois un opérateur unaire (test si une option est définie) et binaire (ou), donc les choses comme [ ! -o monitor ]sont ambiguës. Teste-t-il que l' monitoroption n'est pas définie, ou que les chaînes sont !ou monitornon vides? Les règles POSIX décident: ce dernier (c'est différent avec [[ ! -o monitor ]]).
Stéphane Chazelas
30

[ -n ]n'utilise pas le -ntest.

L' -nentrée [ -n ]n'est pas du tout un test. Lorsqu'il n'y a qu'un seul argument entre [et ], cet argument est une chaîne qui est testée pour voir si elle est vide. Même lorsque cette chaîne a un début -, elle est toujours interprétée comme un opérande et non comme un test. Étant donné que la chaîne -nn'est pas vide - il contient deux caractères, -et n, non nul characters-- [ -n ]evalue à true.

Comme le dit Ignacio Vazquez-Abrams , où se stringtrouve un seul argument, le test effectué sur stringin est le même que le test effectué sur it par . Quand cela arrive , rien de spécial ne se produit. Le dans et la seconde en sont simplement des chaînes testé pour le vide.[ string ][ -n string ]string-n-n[ -n ]-n[ -n -n ]

Lorsqu'il n'y a qu'un seul argument entre [et ], cet argument est toujours une chaîne à tester pour le non-vide, même s'il se trouve être nommé de la même manière qu'un test. De même, lorsqu'il y a deux arguments entre [et ]et que le premier l'est -n, le second est toujours une chaîne à tester pour la non-absence, même s'il se trouve être nommé de la même manière qu'un test. C'est simplement parce que la syntaxe de [insiste sur le fait qu'un seul argument entre [et ]ou après -nest un opérande de chaîne.

Pour la même raison qui [ -n ]n'utilise pas le -ntest, [ -z ]n'utilise pas le -ztest.


Vous pouvez en savoir plus sur [en bashen examinant l'aide correspondante. Notez que c'est un shell intégré :

$ type [
[ is a shell builtin

Ainsi, vous pouvez exécuter help [pour obtenir de l'aide à ce sujet:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Pour plus d'informations, notamment sur les tests pris en charge et leur fonctionnement, vous devrez consulter l'aide test. Lorsque vous exécutez la commande help test, vous obtenez une liste détaillée. Plutôt que de tout reproduire, voici la partie sur les opérateurs de chaînes:

      -z STRING      True if string is empty.

      -n STRING
         STRING      True if string is not empty.

      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

Notez cela -n STRINGet STRINGfaites la même chose: ils testent si la chaîne STRINGn'est pas vide.

Eliah Kagan
la source
2
Bonne explication
Sergiy Kolodyazhnyy
1
Peut-être aussi mentionner que cela a des implications pour les variables non citées qui ne sont pas définies, comme ceci: paste.ubuntu.com/25831047
Sergiy Kolodyazhnyy
1
@SergiyKolodyazhnyy Je pense que cela pourrait être mieux dans une réponse séparée. Les variables qui ne sont pas définies, ou définies mais vides, ou qui contiennent uniquement des IFSespaces blancs, le feront; les variables qui contiennent plusieurs mots au lieu de zéro produisent un autre effet et peuvent entraîner l'exécution d'un test complètement différent. Les chaînes qui apparaissent littéralement avec l'intention d'être des opérandes ne fonctionneront correctement que si elles ne contiennent ni espaces ni tabulations ou si elles sont entre guillemets. La réponse deviendrait beaucoup plus longue et compliquée si je couvrais ce sujet de manière accessible. Si vous décidez de poster une réponse, n'hésitez pas à @ me ici à ce sujet!
Eliah Kagan
@Eliah Kagan, votre réponse est plus complète et détaillée, bien que la réponse d'Ignacio Vazquez-Abrams ait répondu à ma question.
Ravi Kumar
1
Je pense que vous manquez la raison pour laquelle il est et doit être de cette façon: sinon, ne [ "$var" ]serait jamais utilisable comme test car $varpourrait s'étendre à -n.
R ..
12

[ -n ]est vrai car la [commande (alias la testcommande) agit sur le nombre d'arguments qui lui sont donnés. S'il ne reçoit qu'un seul argument, le résultat est "vrai" si l'argument est une chaîne non vide. "-n" est une chaîne de 2 caractères, non vide, donc "true".

glenn jackman
la source