270 votes

Un opérateur "et" pour une instruction "si" en Bash

J'essaie de créer un simple script Bash script pour vérifier si le site web est en panne et pour une raison quelconque, l'opérateur "and" ne fonctionne pas :

#!/usr/bin/env bash

WEBSITE=domain.com
SUBJECT="$WEBSITE DOWN!"
EMAILID="an@email.com"
STATUS=$(curl -sI $WEBSITE | awk '/HTTP\/1.1/ { print $2 }')
STRING=$(curl -s $WEBSITE | grep -o "string_to_search")
VALUE="string_to_search"

if [ $STATUS -ne 200 ] && [[ "$STRING" != "$VALUE" ]]; then
    echo "Website: $WEBSITE is down, status code: '$STATUS' - $(date)" | mail -s "$SUBJECT" $EMAILID
fi

L'opérateur "-a" ne fonctionne pas non plus :

if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]

Pourriez-vous également nous indiquer quand l'utiliser ?

  • crochets simples et doubles
  • parenthèse

?

387voto

William Pursell Points 56211

Ce que vous avez devrait fonctionner, à moins que ${STATUS} est vide. Il serait probablement préférable de faire :

if ! [ "${STATUS}" -eq 200 ] 2> /dev/null && [ "${STRING}" != "${VALUE}" ]; then

ou

if [ "${STATUS}" != 200 ] && [ "${STRING}" != "${VALUE}" ]; then

C'est difficile à dire, puisque vous ne nous avez pas montré exactement ce qui ne va pas avec votre script.

Avis personnel : ne jamais utiliser [[ . Il supprime les messages d'erreur importants et n'est pas transférable à d'autres shells.

70voto

AnshBikram Points 1638

Essayez ceci :

if [ "${STATUS}" -ne 100 -a "${STRING}" = "${VALUE}" ]

ou

if [ "${STATUS}" -ne 100 ] && [ "${STRING}" = "${VALUE}" ]

35voto

BrenanK Points 577

Essayez ceci :

if [ $STATUS -ne 200 -a "$STRING" != "$VALUE" ]; then

15voto

Jo So Points 4602

Citation :

L'opérateur "-a" ne fonctionne pas non plus :

if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]

Pour une explication plus détaillée : [ y ] ne sont pas des mots réservés à Bash. Les mots if introduit une condition à évaluer par un job (la condition est vraie si la valeur de retour du job est 0 ou faux dans le cas contraire).

Pour les tests triviaux, il y a l'option test ( man test ).

Comme certains trouvent des lignes comme if test -f filename; then foo bar; fi etc. ennuyeux, sur la plupart des systèmes, vous trouverez un programme appelé [ qui n'est en fait qu'un lien symbolique vers le fichier test programme. Quand test est appelé [ vous devez ajouter ] comme dernier argument de position.

Así que if test -f filename est fondamentalement le même (en termes de processus créés) que if [ -f filename ] . Dans les deux cas, le test sera lancé, et les deux processus devraient se comporter de manière identique.

Voici votre erreur : if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]] sera analysé comme if + un travail, le travail étant tout ce qui n'est pas l'activité de l'entreprise. if elle-même. Le travail n'est qu'un simple commande (Bash parle de quelque chose qui résulte d'un processus unique), ce qui signifie que le premier mot ( [ ) est la commande et le reste ses arguments positionnels. Il reste des arguments après le premier ] .

Pas non plus, [[ est en effet un mot-clé Bash, mais dans ce cas, il n'est analysé que comme un argument de commande normal, parce qu'il ne se trouve pas au début de la commande.

4voto

Mark Reed Points 23817

Tout d'abord, il n'est pas nécessaire de mettre les noms de variables en majuscules. En général, les noms de variables en majuscules doivent être réservés à ceux qui sont utilisés dans le cadre d'un projet. export dans l'environnement au profit d'autres exécutables.

Deuxièmement, ce que vous avez avec && devrait fonctionner, mais elle n'est pas très résistante face à une variable d'état qui peut être vide ou non numérique.

Enfin, le -a n'a pas fonctionné car -a fait partie de la syntaxe de test / [ ... ] ; c'est parti à l'intérieur les crochets.

Fondamentalement, la chose la plus importante à savoir sur les conditionnelles dans le shell est que ce ne sont que des ordres . Cette expression si :

if foo; then
  echo true
else
  echo false
fi

Fait exactement la même chose que celui-ci :

foo
if [ $? -eq 0 ]; then
  echo true
else
  echo false
fi

Ainsi, après le if (ou while ou until ) peut être n'importe quelle commande.

Il en va de même pour le && y || les opérateurs aussi : les éléments situés de part et d'autre ne sont que des commandes. La séquence foo && bar exécute la commande foo et ensuite, seulement si cette commande s'est terminée avec le statut 0, il exécute bar . (Il s'agit donc en fait d'une abréviation de if foo; then bar; fi .) De même, foo || bar courses foo puis, si cette commande se termine par non nul statut, exécutions bar . (Il s'agit donc en fait d'une abréviation de if ! foo; then bar; fi .)

C'est la principale chose qui échappe à de nombreux programmeurs débutants. L'interpréteur de commandes n'a pas d'expressions en soi, juste des commandes à exécuter.

Cependant, comme vous souhaitez souvent effectuer des comparaisons et d'autres opérations qui seraient des expressions booléennes dans les langages de programmation classiques non shell, il existe une commande spéciale que vous pouvez lancer pour les effectuer. Il s'agissait autrefois d'une commande binaire distincte, mais elle est aujourd'hui intégrée à l'interpréteur de commandes. Son vrai nom est test mais il peut également être invoqué sous la forme [ Dans ce cas, il se plaindra si son dernier argument n'est pas un ] . Les arguments à l'intérieur des crochets ou après test constitue une expression à évaluer, et la commande test sort avec le statut 0 si l'expression est vraie, et non nul si elle est fausse. Il s'agit donc du genre de chose qui est normalement ce qu'un if attend dans d'autres langages de programmation en une commande que vous pouvez exécuter, la traduisant ainsi pour l'interpréteur de commandes.

L'apparition de [ ... ] peut être trompeur ; il est en fait analysé comme s'il s'agissait de l'orthographe test avec le même traitement en ligne de commande que n'importe quelle commande du shell. Cela signifie que si vous essayez de comparer des choses en utilisant < ou > ils seront interprétés comme des redirections. Essayez d'utiliser ( y ) pour le regroupement et vous créerez un sous-shell (et générerez probablement une erreur de syntaxe en le faisant au milieu des arguments d'une commande). Essayez de combiner des expressions avec && y || à l'intérieur des crochets et vous terminerez la commande prématurément, ce qui entraînera à nouveau une erreur de syntaxe.

Vous pouvez toujours utiliser && y || fuera de des crochets ; à ce stade, vous exécutez simplement plusieurs instances de la commande de test au lieu d'une seule. Mais vous pouvez également utiliser -a y -o tant qu'ils sont à l'intérieur les crochets. Ces deux pipelines produisent donc le même résultat :

[ "$status" -ne 200 -a "$string" != "$value" ]

[ "$status" -ne 200 ] && [ "$string" != "value" ]

La différence est que la première est vérifiée par une instance de la fonction test tandis que le second en exécute deux.

Le shell Korn a introduit une nouvelle version de la commande test qui s'écrit avec deux crochets au lieu d'un, entre lesquels se trouve un environnement syntaxique spécial qui n'est pas analysé de la même manière qu'une commande normale. Entre les doubles crochets, vous pouvez utiliser en toute sécurité des expansions de paramètres non citées, des parenthèses non citées pour le regroupement, < y > pour la comparaison de chaînes de caractères (s'en tenir à -lt y -gt pour une comparaison numérique), et && y || en tant qu'opérateurs booléens. [[ ... ]] ajoute également la possibilité de faire correspondre des motifs globaux ( [[ foo == f* ]] ) et les expressions régulières ( [[ foo =~ ^f ]] ).

Les shells modernes tels que Bash et Zsh ont hérité de cette construction de Ksh, mais elle ne fait pas partie de la spécification POSIX. Si vous êtes dans un environnement où vous devez être strictement conforme à POSIX, ne l'utilisez pas ; sinon, c'est une question de préférence personnelle.

Notez que l'expression de substitution arithmétique $(( ... )) es fait partie de POSIX, et ses règles sont très similaires à celles de la norme [[ ... ]] . La principale différence est que les tests d'égalité et d'inégalité (qui produisent ici une valeur numérique, 0 pour faux et 1 pour vrai) sont effectués numériquement au lieu d'être effectués par chaîne de caractères : [[ 10 < 2 ]] est vrai, mais $(( 10 < 2 )) produit 0.

Là encore, les shells modernes se sont appuyés sur la spécification POSIX pour ajouter une fonction indépendante $ -Version allégée de (( ... )) qui fonctionne comme une citation automatique let que vous pouvez utiliser dans if expressions si vous avez affaire à des chiffres. Ainsi, votre première condition pourrait également s'écrire (( status != 200 )) . Encore une fois, en dehors de l'obligation de s'en tenir à POSIX, c'est une question de préférence personnelle. J'aime utiliser (( ... )) chaque fois que j'ai affaire à des chiffres, mais d'autres préfèrent s'en tenir à [ ou [[ et utiliser les opérateurs numériques comme -lt .

Pour ce que cela vaut, voici comment je réécrirais votre code :

website=domain.com
subject="$website DOWN!"
email=an@email.com
value="string_to_search"
status=$(curl -sI "$website" | awk '/HTTP\/1.1/ { print $2 }')
string=$(curl -s "$website" | grep -o "$value")

if (( status != 200 )) && [[ $string != $value ]]; then
    mail -s "$subject" "$email" <<<"Website: $website is down, status code: '$status' - $(date)"
fi

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X