Comment faire bash abandonner l'exécution d'un script en cas d'erreur de syntaxe?

15

Pour être sûr, j'aimerais que bash abandonne l'exécution d'un script s'il rencontre une erreur de syntaxe.

À ma grande surprise, je ne peux pas y arriver. ( set -ene suffit pas.) Exemple:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

Résultat (bash-3.2.39 ou bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Eh bien, nous ne pouvons pas vérifier $?après chaque instruction pour détecter les erreurs de syntaxe.

(Je m'attendais à un tel comportement sûr d'un langage de programmation raisonnable ... peut-être que cela doit être signalé comme un bug / souhait de bash développeurs)

Plus d'expériences

if ça ne fait aucune différence.

Suppression if:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Résultat:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Peut-être que cela est lié à l'exercice 2 de http://mywiki.wooledge.org/BashFAQ/105 et a quelque chose à voir avec (( )). Mais je trouve toujours déraisonnable de continuer à exécuter après une erreur de syntaxe.

Non, (( ))cela ne fait aucune différence!

Il se comporte mal même sans le test arithmétique! Juste un script simple et basique:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Résultat:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 
imz - Ivan Zakharyaschev
la source
set -en'est pas suffisant car votre erreur de syntaxe se trouve dans une ifinstruction. N'importe où ailleurs devrait abandonner le script.
jordanm
@jordanm Ok, cela peut expliquer pourquoi cela set -en'a pas fonctionné. Mais ma question a toujours un sens. Est-il possible d'interrompre une erreur de syntaxe?
imz - Ivan Zakharyaschev
@jordanm Supprimé "si"; ne fait aucune différence (mise à jour de ma question).
imz - Ivan Zakharyaschev

Réponses:

9

Envelopper le tout dans une fonction semble faire l'affaire:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Résultat:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Bien que je ne sache pas pourquoi - peut-être que quelqu'un d'autre peut expliquer?

ahilsend
la source
2
Votre définition de fonction est maintenant analysée et évaluée, et elle échoue.
tripleee du
Bonne solution! BTW, il n'interrompt pas le programme entier dans ce cas aussi. J'ai ajouté echo 'Bad2: has not aborted the execution after bad main!'en dernier à votre exemple, et la sortie est: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: ligne 6: #: erreur de syntaxe: opérande attendu ( le jeton d'erreur est "#") Bad2: n'a pas interrompu l'exécution après une mauvaise connexion principale! $
imz - Ivan Zakharyaschev
Mais nous ne devons pas simplement ajouter une ligne, nous devons tout mettre à l'intérieur d'une fonction.
imz - Ivan Zakharyaschev
@tripleee Oui, il semble que l'analyse de la fonction échoue, elle n'est donc pas terminée, mais l'ensemble du programme n'est pas abandonné dans ce cas (ce n'est donc probablement pas l'effet de la sortie sur erreur).
imz - Ivan Zakharyaschev
6

Vous vous trompez probablement sur la véritable signification de set -e. Une lecture attentive de la sortie des help setspectacles:

-e  Exit immediately if a command exits with a non-zero status.

Il -es'agit donc de l'état de sortie des commandes non nulles, pas des erreurs de syntaxe dans votre script.

En général, il est considéré comme une mauvaise pratique à utiliser set -e, car toutes les erreurs (c'est-à-dire tous les retours non nuls des commandes) doivent être intelligemment gérées par le script (pensez à un script robuste, pas à ceux qui se déchaînent après avoir entré un nom de fichier avec un espace ou qui commence par un hypen).

Selon le type d'erreur de syntaxe, le script peut même ne pas être exécuté du tout. Je ne connais pas suffisamment bash pour dire exactement quelle classe d'erreurs de syntaxe (si seulement elles peuvent être classées) pourrait conduire à un avortement immédiat du script ou non. Peut-être que certains gourous Bash se joindront à eux et clarifieront tout.

J'espère seulement avoir clarifié la set -edéclaration!

A propos de votre souhait:

Je m'attendais à un tel comportement sûr d'un langage de programmation sensible ... peut-être que cela doit être signalé comme un bogue / souhait aux développeurs bash

La réponse est définitivement non! car ce que vous avez observé ( set -ene pas répondre comme vous vous y attendiez) est en fait très bien documenté.

gniourf_gniourf
la source
Je voulais dire que l'absence d'une telle fonctionnalité est un problème. Je ne voulais pas me concentrer sur set -e- c'est juste un peu proche de mes objectifs, c'est pourquoi il est mentionné et utilisé ici. Ma question ne porte pas sur set -ela sécurité de bash si elle ne peut pas être interrompue en cas d'erreur de syntaxe. Je cherche un moyen de faire toujours avorter les erreurs de syntaxe.
imz - Ivan Zakharyaschev
4

Vous pouvez faire vérifier le script lui-même en mettant quelque chose comme

bash -n "$0"

près du haut du script - après set -emais avant tout élément significatif de code.

Je dois dire que cela ne semble pas très robuste, mais si cela fonctionne pour vous, c'est peut-être acceptable.

tripleee
la source
Excellent! Ou, sans set -e: bash -n "$0" || exit
Daniel S
0

Tout d'abord, le (( ))in bash est utilisé comme calculs arithmétiques, à ne pas utiliser dans le if ... utilisez le []pour cela.

Deuxièmement, le ${a[#]}est bizarre et c'est pourquoi donne des erreurs ... le #n'a pas de signification de tableau

Je ne sais pas ce que vous voulez faire avec ça, mais je suppose que vous voulez connaître le nombre de champs, donc vous voulez ${#a[*]}plutôt

Enfin, lors de la comparaison d'entiers, le -eqest recommandé sur ==(utilisé pour les chaînes). la ==volonté fonctionne également, mais -eqest recommandée.

alors tu veux:

if [ ${#a[*]} -eq 2 ]; then 
higuita
la source
4
Ce n'est pas vrai. Il est courant d'utiliser le ((mot - clé avec le ifmot - clé. Par exemple if (( 5 * $b > 53 )),. À moins que vous ne visiez la portabilité avec des obus plus anciens, [[c'est généralement préférable à [.
2
Oui, je suis d'accord avec @Evan - [[et ((ont été conçus spécifiquement comme des tests légers à utiliser avec "if", etc. - contrairement à cela [, ils ne génèrent jamais un sous-processus pour évaluer la condition.
imz - Ivan Zakharyaschev
1
[est un bash intégré. Les coquilles plus anciennes s'attendent à ce que ce soit son propre programme.