Quelle est la différence de -a et -e dans les expressions conditionnelles de bash?

12

De man bash:

CONDITIONAL EXPRESSIONS
[...]
       -a file
              True if file exists.
[...]
       -e file
              True if file exists.
  1. Quelle est donc la différence entre [ -a $FILE ]et [ -e $FILE ], le cas échéant?
  2. S'il n'y a pas de réelle différence, pourquoi existe-t-il deux drapeaux dans le même but?
polym
la source
Seuls les développeurs sauraient # 2 --- à moins qu'ils ne partagent leur raisonnement avec la communauté.
Belmin Fernandez

Réponses:

13

Dans bash, avec le contexte de la testcommande deux arguments , -a fileet -e filesont les mêmes. Mais ils ont une certaine différence, car -ac'est aussi un opérateur binaire.

-eunary est défini par POSIX, mais -aunary ne l'est pas. POSIX définit uniquement le -abinaire (voir test POSIX).

POSIX définit le testcomportement de trois arguments :

3 arguments:

  • Si $ 2 est un primaire binaire, effectuez le test binaire de $ 1 et $ 3.

  • Si $ 1 est '!', Annulez le test à deux arguments de $ 2 et $ 3.

  • Si $ 1 est '(' et 3 $ est ')', effectuez le test unaire de 2 $. Sur les systèmes qui ne prennent pas en charge l'option XSI, les résultats ne sont pas spécifiés si $ 1 est '(' et $ 3 est ')'.

  • Sinon, produisez des résultats non spécifiés.

Cela -aconduit également à un résultat étrange:

$ [ ! -a . ] && echo true
true

-aest considéré comme un opérateur binaire dans le contexte de trois arguments. Voir la question E1 de la FAQ Bash . POSIX mentionne également que cela -aprovient de KornShell mais a été changé plus tard en -ecar cela rend la confusion entre -abinaire et -aunaire.

Le -e primaire, possédant des fonctionnalités similaires à celles fournies par le shell C, a été ajouté car il fournit le seul moyen pour un script shell de savoir si un fichier existe sans essayer d'ouvrir le fichier. Étant donné que les implémentations sont autorisées à ajouter des types de fichiers supplémentaires, un script portable ne peut pas utiliser:

test -b foo -o -c foo -o -d foo -o -f foo -o -p foo

pour savoir si foo est un fichier existant. Sur les systèmes BSD historiques, l'existence d'un fichier peut être déterminée par:

test -f foo -o -d foo

mais il n'y avait pas de moyen facile de déterminer qu'un fichier existant était un fichier normal. Une première proposition a utilisé le KornShell -a primaire (avec la même signification), mais cela a été changé en -e parce qu'il y avait des inquiétudes concernant la forte probabilité que les humains confondent le -a primaire avec l'opérateur binaire -a.

-abinaire est également marqué comme obsolète, car il conduit à une expression ambiguë, qui a plus de 4 arguments. Avec ces expressions> 4 arguments, POSIX définit que le résultat n'est pas spécifié.

cuonglm
la source
Cool à savoir! Maintenant c'est un peu plus clair :)!
polym
9

.1. Quelle est donc la différence entre [-a $ FILE] et [-e $ FILE], le cas échéant?

Il n'y a aucune différence.

En ligne 505-507avec test.cla version bash 4.2.45(1)-release:

case 'a':           /* file exists in the file system? */
case 'e':
  return (sh_stat (arg, &stat_buf) == 0);

Cela indique qu'il n'y a pas de réelle différence entre les deux drapeaux.

.2. S'il n'y a pas de réelle différence, pourquoi existe-t-il deux drapeaux dans le même but?

Voir la réponse de gnouc .

polym
la source
3
D'un autre côté, étant donné qu'il -as'agit également d' un opérateur booléen (et) dans bash, il semble sujet à des bogues d'ajouter cette signification supplémentaire qui est complètement redondante; Je suppose que c'est plus lié à la compatibilité avec une autre implémentation et / ou la compatibilité avec des versions antérieures qui n'en avaient pas -e.
celtschk
@celtschk - ce n'est pas bugprone, mais l'analyseur du shell peut l'être. POSIX spécifie les booléens -aet . Mais certains obus (comme ) ont réussi à distinguer un argument d'un opérateur et les résultats n'étaient pas fiables. Depuis lors, POSIX a exigé que tout shell compatible gère au moins 4 arguments entre crochets. -o[ test ]bash[ test ][ test ]
mikeserv
5

La meilleure réponse que j'ai trouvée est celle-ci, à partir d'une question StackOverflow:

-aest obsolète, il n'est donc plus répertorié dans la page de manuel pour /usr/bin/test, mais toujours dans celui de bash. Utilisez -e. Pour un simple «[», la fonction intégrée bash se comporte de la même manière que la fonction testintégrée bash, qui se comporte de la même manière /usr/bin/[que /usr/bin/test ( et l'un est un lien symbolique vers l'autre). Notez que l'effet de -adépend de sa position: s'il est au début, cela signifie file exists. Si c'est au milieu de deux expressions, cela signifie logique and.

[ ! -a /path ] && echo existsne fonctionne pas, comme le souligne le manuel bash qui -aest considéré comme un opérateur binaire, et donc ce qui précède n'est pas analysé comme un negate -a ..mais comme if '!' and '/path' is true(non vide). Ainsi, votre script sort toujours "-a"(qui teste réellement les fichiers), et "! -a"qui est en fait un binaire andici.

Car [[, -an'est plus utilisé comme binaire and(y &&est utilisé), son but unique est donc de rechercher un fichier là-bas (bien qu'il soit obsolète). La négation fait donc ce que vous attendez.

jimm-cl
la source