Question Bash Script: intercepter les erreurs dans un bloc d'instructions


J'ai un grand ensemble de règles iptables que je gère avec mon propre script bash. La plupart des commandes du script sont des commandes iptables simples, à un seul état. J'essaie d'améliorer le script en ajoutant la sortie succès / échec lors de l'exécution du script.

J'ai le script divisé en différentes sections. Un exemple serait la section de la chaîne FORWARD, où toutes les règles sont appliquées à la chaîne FORWARD. Au début de la section, je signale que le script a commencé à appliquer les règles FORWARD, et à la fin, je veux afficher si toutes les règles ont été appliquées avec succès ou si aucune n'a fonctionné. Voici l'idée de base:

#Start FORWARD section
echo -ne "Applying FORWARD rules..."

#rule 1
/sbin/iptables -A FOWRARD...

#rule 2
/sbin/iptables -A FORWARD...

echo -ne "\t\t\t[OK]\n"

Ce que je veux faire, c'est attraper les sorties ou les erreurs qui peuvent résulter de chaque commande iptables et les stocker dans un tableau ou quelque chose. Ensuite, à la fin du bloc, utilisez une instruction if pour évaluer le tableau pour voir s'il y a des erreurs. Sinon, indiquez le statut [OK], le cas échéant, indiquez le statut [FAILED] et affichez l'erreur correspondante.

Est-il possible de le faire pour tout le bloc de règles sans envelopper chaque règle iptables dans un if [$? ! = 0] expression?


4
2017-07-19 17:54


origine


Cela semble mieux convenir à Unix & Linux, ou à StackOverflow, en raison de sa nature liée à Linux, mais pas de la nature d'Ubuntu. - Thomas Ward♦
Si vos règles sont statiques ou peuvent être regroupées, considérez iptables-apply comme il n'appliquera pas les règles si l'une d'elles échoue. - Lekensteyn
Vous pourriez utiliser un trap .. ERR pour détecter les erreurs et les signaler, ou même set -e pour annuler le script si des erreurs non détectées sont rencontrées. - DopeGhoti


Réponses:


Vous pouvez utiliser le trap commande intégrée shell pour faire appel à une fonction de gestionnaire si une commande a un statut de sortie non nul. Vous pouvez transmettre les informations nécessaires, telles que le numéro de ligne et le statut de sortie, à votre fonction de gestionnaire d'erreurs.

Exemple:

#!/bin/bash

handle_error() {
    echo "FAILED: line $1, exit code $2"
    exit 1
}

trap 'handle_error $LINENO $?' ERR 

# your commands here
# ...

echo "OK"

10
2017-07-19 20:07



Dans toutes mes années de bash je n'ai jamais entendu parler de la commande de piège! - Marco Ceppi♦
vous devrez peut-être aussi: set -o errtrace au plus haut niveau du script, si vous voulez aussi que les fonctions de ce script appellent ce piège en cas d'erreurs (sinon, à l'intérieur des fonctions, cette interruption ne serait pas appelée) - Olivier Dulac


Le script ci-dessous définira le recordfailure fonction qui devrait être ajoutée aux commandes comme indiqué dans l'exemple après la définition de la fonction. Si la commande renvoie une valeur différente de zéro, le flux d'erreur standard est consigné. Enfin, à la fin, le journal des erreurs est vérifié et [OK] ou [FAIL] est imprimé en conséquence.

#!/bin/bash

# Redirect file descriptor #3 to standard output (used in recordfailure)
exec 3>&1

# create an array for holding failures
declare -a failures

# recordfailure command arg1 arg2 ... argN
recordfailure() {
    local error retval
    # Run the command and store error messages (output to the standard error
    # stream in $error, but send regular output to file descriptor 3 which
    # redirects to standard output
    error="$("$@" 2>&1 >&3)"
    retval=$?
    # if the command failed (returned a non-zero exit code)
    if [ $retval -gt 0 ]; then
        if [ -z "$error" ]; then
            # create an error message if there was none
            error="Command failed with exit code $retval"
        fi
        # uncomment if you want the command in the error message
        #error="Command $* failed: $error"

        # append the error to $failures, ${#failures[@]} is the length of
        # the array and since array start at index 0, a new item is created
        failures[${#failures[@]}]="$error"
        # uncomment if you want to show the error immediately
        #echo "$error"
    fi
}
recordfailure iptables -A FORWARD ...
recordfailure iptables -A FORWARD ...
# if the length of the failures array equals 0 (no items) everything is OK
if [ ${#failures[@]} -eq 0 ]; then
    echo "[OK]"
else
    echo "[FAIL]"
    # list every error
    for failure in "${failures[@]}"; do
        # optionally color it, format it or whatever you want to do with it
        echo "error: $failure"
    done
fi

Si vous ne souhaitez pas afficher de sortie standard, supprimez exec 3>&1 et remplacer >&3 par >/dev/null.


0
2017-07-19 19:37



Ne pas tester $? pour true ou false, lancez simplement la commande dans le if en premier lieu if ! error=$("$@" ...); then .... Oh et vous avez une virgule dans la local commander. - geirha
@geirha: J'utilise la valeur de retour en cas d'absence de message d'erreur. Si j'utilise if ! error=$("$@" 2>&1 >&3), Je ne peux pas évaluer un autre si et afficher la valeur de retour. - Lekensteyn
Sûr que vous pouvez, if vérifie la valeur de retour de la commande que vous exécutez, pas si elle génère quelque chose à stderr. - geirha


Tu veux dire quelque chose comme ca?

ok=1

#Start FORWARD section
echo -ne "Applying FORWARD rules..."

#rule 1
/sbin/iptables -A FOWRARD... || ok=0

#rule 2 
/sbin/iptables -A FORWARD... || ok=0

echo -ne "\t\t\t"
if [ $ok -eq 1 ]; then
  echo "[OK]"
else
  echo "[fail]"
fi

-1
2017-07-19 18:52