Est-ce que test ou [ou [[est plus portable à la fois entre les obus bash et entre les autres obus?

44

Je vois que je peux faire

$ [ -w /home/durrantm ] && echo "writable"
writable

ou

$ test -w /home/durrantm && echo "writable"
writable

ou

$ [[ -w /home/durrantm ]] && echo "writable"
writable

J'aime utiliser la troisième syntaxe. Sont-ils équivalents à tous égards et pour tous les cas négatifs et marginaux? Existe-t-il des différences de portabilité, par exemple entre bash sur Ubuntu et sur OS X ou des versions bash antérieures / récentes, par exemple avant / après 4.0 et les expressions sont-elles développées de la même manière?

Michael Durrant
la source
2
Pour [ … ]vs [[ … ]]vs test …, il y a des réponses plus complètes dans cette question principalement en double .
Gilles 'SO- arrête d'être méchant'
Pour plus d'informations sur le problème de test de l'écriture d'un fichier, voir aussi Comment tester de manière non invasive l'accès en écriture à un fichier?
G-Man dit 'Réintégrez Monica'
1
Il y a un dicton qui dit: "il n'y a pas de code portable, seulement du code qui a été porté". Mon conseil à ce sujet: utilisez le formulaire le plus lisible (probablement [[...]]) et essayez-le sur toutes les plates-formes que vous souhaitez prendre en charge. Il n’est pas utile d’obscurcir vos scripts pour qu’ils s’exécutent sur des plateformes anciennes que ni vous ni votre public cible n’utilisez. Cela rendra simplement votre code difficile à lire, introduira des bogues inutiles et peut-être même des problèmes de sécurité (comme ce fut le cas pour openssl).
stefan.schwetschke

Réponses:

31

[est synonyme de testcommande et est simultanément une commande intégrée et séparée de bash . Mais [[est un mot clé bash et ne fonctionne que dans certaines versions. Donc, pour des raisons de portabilité, il vaut mieux utiliser un []ou plusieurstest

[ -w "/home/durrantm" ] && echo "writable"
Costas
la source
2
[POSIX est-il intégré ou Bash est-il intégré?
Sandburg
@Sandburg POSIX est un standard, il ne peut donc pas être intégré. Cela ne définit pas si quelque chose doit être intégré à l'interprète ou pas, mais simplement ce qu'il doit faire. Ces règles s'appliquent aux deux formes testet aux [mêmes. Si un shell peut effectuer l'évaluation par lui-même, cela vous enregistre un processus et le rend un peu plus rapide, mais le résultat doit être identique.
Bachsau
@Bachsau qui ne répond toujours pas à la question de Sandburg sur le point de savoir si le comportement de [est défini par POSIX et donc extrêmement portable (ce qui semble être le but de cette question si je ne me trompe pas)
JamesTheAwesomeDude
@ Sandburg (et pour tous ceux qui tombent sur ça): Oui, ça ressemble aux deux [et test c'est POSIX . Il semble n'y avoir ni "recommandé", bien que la seule différence (?) Que je puisse distinguer entre eux est que test"ne reconnaîtra pas l'argument" - "[en tant que délimiteur indiquant la fin des options]" (? - ce qui implique que " - " est reconnu comme étant une fin d'argumentation [, ce que je ne parviens pas à trouver un bon test. Quoi qu'il en soit, je m'éloigne du sujet. Utilisez ce que vous voulez. IMO, [c'est élégant, mais c'est testclairement une commande)
JamesTheAwesomeDude
35

Oui, il y a des différences. Les plus portables sont testou [ ]. Ceux-ci font tous les deux partie de la spécification POSIXtest .

La if ... ficonstruction est également définie par POSIX et doit être entièrement portable.

Le [[ ]]est une kshfonctionnalité qui est également présente dans certaines versions de bash( toutes les modernes ), zshet peut-être dans d’autres, mais n’est pas présente dans shou dans dashles divers autres coquillages plus simples.

Donc, pour faire vos scripts portables, utiliser [ ], testou if ... fi.

terdon
la source
10
Juste pour noter, bashet zshavoir soutenu [[pendant très longtemps ( bashajouté à la fin des années 90, zshpas plus tard que 2000, et je serais surpris si jamais il manquait de support), il est donc peu probable que vous rencontriez une version sans [[. Il dashest beaucoup plus probable de rencontrer un autre shell compatible POSIX (tel que ).
Chepner
1
Une caractéristique intéressante [[est que les développements de paramètres ne doivent pas être cités: [[-f $file]]event fonctionne si $filecontient des caractères d'espacement.
helpermethod
2
@helpermethod également, expressions régulières intégrées[[ $a =~ ^reg.*exp.*$' ]]
GnP
20

S'il vous plaît noter que ce [] && cmdn'est pas la même chose que la if .. ficonstruction.

Parfois, son comportement est assez similaire et vous pouvez utiliser [] && cmdau lieu de if .. fi. Mais seulement parfois. Si vous avez plus d'une commande à exécuter si condition ou si vous devez if .. else .. fiêtre prudent et effacer la logique.

Quelques exemples:

[ -z "$VAR" ] && ls file || echo wiiii

N'est-ce pas la même chose que

if [ -z $VAR ] ; then
  ls file
else
  echo wiii
fi

car si lséchouera, echosera exécuté ce qui ne se produira pas avec if.

Un autre exemple:

[ -z "$VAR" ] && ls file && echo wiii

n'est pas la même chose que

if [ -z "$VAR" ] ; then
   ls file
   echo $wiii
fi

bien que cette construction agisse de la même manière

[ -z "$VAR" ] && { ls file ; echo wiii ; }

s'il vous plaît noter ;après l'écho est important et doit être là.

Donc, reprenant la déclaration ci-dessus, nous pouvons dire

[] && cmd == si la première commande réussit, exécute la suivante

if .. fi == si condition (qui peut également être la commande test), puis exécuter la ou les commandes

Donc, pour la portabilité entre [et [[utiliser [seulement.

ifest compatible POSIX. Donc, si vous devez choisir entre [et ifchoisissez de regarder votre tâche et votre comportement attendu.

se ruer
la source
Je suis probablement juste en train d'être dense, mais je ne vois pas quelle serait la différence ... pourriez-vous s'il vous plaît donner un exemple où ces deux constructions donneraient des résultats différents?
Evilsoup
1
@evilsoup, mis à jour. Peut-être que je ne suis pas le meilleur explicateur, même si j'espère que ce sera clair maintenant.
précipiter
1
Très bons points, précipitez-vous. Je suppose une forme sûre de votre 1er exemple serait: [ -z "$VAR" ] && { ls file; true; } || echo wiiii. C'est un peu plus verbeux, mais c'est quand même plus court que la if...ficonstruction.
PM 2Re
Faites en sorte que la question porte sur [vs [[vs test et non sur si ... fi vs && pour en faire UNE seule question. Malheureusement, cette réponse semble hors de propos. Excuses pour ne pas avoir la question plus ciblée initialement qui a conduit à cela. Vivre et (essayer de) apprendre. :)
Michael Durrant
J'ai voté parce que a) c'est principalement une bonne réponse, mais c'est une réponse à une question différente. b) vous présentez [et en iftant que sous-titres, mais ils ne sont pas. C'est en fait le &&qui remplace le if. [exécute du code et retourne un statut, beaucoup comme lset le grepferait. ifL'exécution des branches dépend du statut de retour de la commande (instruction) donnée après le if, il peut s'agir de n'importe quelle commande (instruction). &&exécute l'instruction suivante uniquement si l'instruction précédente a renvoyé 0, à la manière d'un simple if..then..fi.
GnP
9

En fait, &&c’est le qui remplace if, et non le test: une ifinstruction dans les scripts de shell vérifie si une commande a renvoyé un état de sortie "réussi" (zéro); dans votre exemple, la commande est [.

Il y a donc deux choses que vous faites varier ici: la commande utilisée pour exécuter le test et la syntaxe utilisée pour exécuter du code en fonction du résultat de ce test.

Commandes de test:

  • testest une commande standardisée permettant d’évaluer les propriétés des chaînes et des fichiers; dans votre exemple, vous exécutez la commandetest -w /home/durrantm
  • [est un alias de cette commande, également normalisé, qui a un dernier argument obligatoire ]pour ressembler à une expression entre crochets; ne vous laissez pas berner, c'est toujours juste une commande (vous pouvez même trouver que votre système a un fichier appelé /bin/[)
  • [[est une version étendue de la commande de test intégrée à certains shells, mais ne faisant pas partie du même standard POSIX; il inclut des options supplémentaires que vous n'utilisez pas ici

Expressions conditionnelles:

  • L' &&opérateur ( normalisé ici ) effectue une opération AND logique en évaluant deux commandes et en renvoyant 0 (ce qui représente la valeur vraie) si elles renvoient toutes les deux 0; il n'évaluera la deuxième commande que si la première a retourné zéro, elle peut donc être utilisée comme simple condition
  • La if ... then ... ficonstruction ( normalisée ici ) utilise la même méthode pour juger de la "vérité", mais permet une liste composée d'instructions dans la thenclause, plutôt que la seule commande fournie par un &&court-circuit, et fournit des clauses elifet des elsearticles difficiles à écrire. en utilisant seulement &&et ||. Notez qu'il n'y a pas de crochets autour de la condition dans une ifinstruction .

Les exemples suivants sont donc également portables, et entièrement équivalents, de votre exemple:

  • test -w /home/durrantm && echo "writable"
  • [ -w /home/durrantm ] && echo "writable"
  • if test -w /home/durrantm; then echo "writable"; fi
  • if [ -w /home/durrantm ]; then echo "writable"; fi

Bien que les éléments suivants soient également équivalents, mais moins portables, en raison de la nature non standard de [[:

  • [[ -w /home/durrantm ]] && echo "writable"
  • if [[ -w /home/durrantm ]]; then echo "writable"; fi
IMSoP
la source
Oui, j'ai supprimé la partie if .... par exemple pour que cette question soit une.
Michael Durrant
7

Pour la portabilité, utilisez test/ [. Mais si vous n'avez pas besoin de la portabilité, par souci de santé mentale et vis-à-vis des autres lecteurs de votre script, utilisez-le [[. :)

Voir aussi What is the difference between test, [ and [[ ?dans le BashFAQ .

PM 2Ring
la source
7

Si vous souhaitez une portabilité hors du monde semblable à Bourne, alors:

test -w /home/durrantm && echo writable

est le plus portable. Cela fonctionne dans les coquilles de la Bourne, cshet les rcfamilles.

test -w /home/durrantm && echo "writable"

la production physique au "writable"lieu de writabledans les coquilles de la rcfamille ( rc, es, akanga, où "est pas spécial).

[ -w /home/durrantm ] && echo writable

ne fonctionnerait pas dans les coquilles de cshou des rcfamilles sur des systèmes sans [commande $PATH(certaines sont connues pour avoir, testmais pas son [alias).

if [ -w /home/durrantm ]; then echo writabe; fi

ne fonctionne que dans les coquillages de la famille Bourne.

[[ -w /home/durrantm ]] && echo writable

ne fonctionne que dans ksh(où il est originaire), zshet bash(tous les 3 dans la famille Bourne).

Aucun ne fonctionnerait dans la fishcoquille où vous avez besoin:

[ -w /home/durrantm ]; and echo writable

ou:

if [ -w /home/durrantm ]; echo writable; end
Stéphane Chazelas
la source
0

La raison la plus importante pour laquelle je choisis de choisir l’un if foo; then bar; fiou l’ autre foo && barest de savoir si le statut de sortie de l’ensemble de la commande est important.

comparer:

#!/bin/sh
set -e
foo && bar
do_baz

avec:

#!/bin/sh
set -e
if foo; then bar; fi
do_baz

Vous pourriez penser qu'ils font la même chose; Toutefois, si fooéchoue (ou est false, selon votre point de vue), alors, dans le premier exemple, do_baz ne sera pas exécuté, car le script sera terminé ... Le set -eshell ordonne à l'enveloppe de se terminer immédiatement si une commande renvoie un statut faux. Très utile si vous faites des choses comme:

cd /some/directory
rm -rf *

Vous ne voulez pas que le script continue à s'exécuter en cas d' cdéchec pour une raison quelconque.

Wurtel
la source
1
Non, un échec foon'abandonnera pas le script dans les deux cas. C'est un cas spécial pour set -e(lorsque la commande est évaluée en tant que condition (à gauche de certaines conditions && / || ou dans les conditions if / while / Until / elsif ...). Un échec barsortirait du shell dans les deux cas.
Stéphane Chazelas
2
Vous voulez cd /some/directory && rm -rf -- *ou cd /some/directory || exit; rm -rf -- *(ne supprime toujours pas les fichiers cachés). Personnellement, je n'aime pas l'idée d'utiliser set -ecomme excuse pour ne pas faire un effort pour écrire le code correct.
Stéphane Chazelas