Enregistrer le code de sortie pour plus tard

15

J'ai donc un petit script pour exécuter des tests.

javac *.java && java -ea Test
rm -f *.class

Maintenant, le problème est que lorsque j'exécute le script ./test, il renvoie un code de sortie de réussite même si le test échoue car il rm -f *.classréussit.

La seule façon dont je pouvais penser à lui faire faire ce que je veux me semble moche:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
if [ "$test_exit_code" != 0 ] ; then false; fi

Mais cela semble être un problème commun - effectuez une tâche, nettoyez, puis renvoyez le code de sortie de la tâche d'origine.

Quelle est la façon la plus idiomatique de le faire (en bash ou simplement en coquilles en général)?

math4tots
la source

Réponses:

5

Vous pouvez envelopper les exitet rmcommandes en une seule commande simple evalcomme:

java ... && java ...
eval "rm -f *.class; exit $?"

La $?valeur de cette façon , lorsqu'elle est transmise à, exitest celle qui lui est affectée immédiatement avant l' evalexécution.

mikeserv
la source
evalest toujours un favori des fans.
mikeserv
23

J'irais avec:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
exit "$test_exit_code"

Pourquoi sauter quand exitest disponible?


Vous pouvez utiliser un trap:

trap 'last_error_code=$?' ERR

Par exemple:

$ trap 'last_error_code=$?' ERR
$ false
$ echo $?
1
$ echo $last_error_code $?
1 0
muru
la source
Ah je suis d'accord c'est mieux que mon original. Mais il ne me semble toujours pas satisfaisant de devoir stocker explicitement le code de sortie dans une variable. N'y a-t-il aucun moyen de «pousser» un code de sortie et de le «ressusciter» plus tard?
math4tots
@ math4tots Essayez la mise à jour.
muru
Donc, avec votre mise à jour, je devrais initialiser last_error_code à zéro, puis revenir à la fin pour avoir un code de sortie différent de zéro si une commande générait une erreur? C'est un truc sympa, mais pour mon script de hack à deux lignes, je pense que je préfère la réponse de @mikeserv.
math4tots
@ math4tots Vous pouvez toujours le faire exit ${last_error_code:=0}.
muru
@avip pour quoi? Elle est déjà entre guillemets simples, donc la variable n'est évaluée que lorsque l'interruption est appelée.
muru
9

Pour autant que je sache, la chose la plus proche que bash a à un try...finallybloc d'un langage de programmation plus semblable à C (ce que vous voudriez probablement s'il était disponible) est la trapconstruction, qui fonctionne comme ceci:

trap "rm -f *.class" EXIT
javac *.java && java -ea Test

Cela exécutera "rm -f * .class" à la fin de votre script. Si vous avez quelque chose de plus complexe à faire, vous pouvez le mettre dans une fonction:

cleanup() {
    ...
}
trap cleanup EXIT
javac *.java && java -ea Test

Si vous êtes si enclin, vous pouvez transformer cela en un idiome assez général qui fonctionne à peu près comme un try...catch...finallybloc en C. Quelque chose comme ceci:

(
  trap "catch_block; exit" ERR
  trap finally_block EXIT
  # contents of try goes here
)

Notez que les parenthèses délimitent un sous-shell; avec cette construction, seul le sous-shell se ferme si une commande échoue, pas tout le script. N'oubliez pas que les sous-coquilles sont quelque peu coûteuses en termes de calcul, alors n'en utilisez pas trop (des centaines). En fonction de votre script, vous pourrez peut-être obtenir le même effet plus efficacement avec les fonctions shell et trap ... RETURN, mais c'est à vous d'enquêter.

David Z
la source