N'arrêtez pas de faire si une commande échoue, mais vérifiez l'état de sortie

22

J'essaie de demander à GNU Make 3.81 de ne pas s'arrêter si une commande échoue (donc je préfixe la commande avec -) mais je veux également vérifier l'état de sortie sur la commande suivante et imprimer un message plus informatif. Cependant mon Makefile ci-dessous échoue:

$ cat Makefile 
all:
    -/bin/false
    ([ $$? -eq 0 ] && echo "success!") || echo "failure!"
$
$ make
/bin/false
make: [all] Error 1 (ignored)
([ $? -eq 0 ] && echo "success!") || echo "failure!"
success!

Pourquoi le Makefile ci-dessus fait-il écho au "succès!" au lieu de "l'échec!" ?

mise à jour:

En suivant et en développant la réponse acceptée, voici comment elle doit être écrite:

failure:                                                                                                                                                                                                                                      
    @-/bin/false && ([ $$? -eq 0 ] && echo "success!") || echo "failure!"                                                                                                                                                                 
success:                                                                                                                                                                                                                                      
    @-/bin/true && ([ $$? -eq 0 ] && echo "success!") || echo "failure!"     
Marcus Junius Brutus
la source
2
Vous voudrez peut-être enquêter sur la .ONESHELL:directive.
Jonathan Leffler
.ONESHELL exécutera tous les blocs de réception dans un seul shell, ce qui a un effet: si la première commande échoue, les suivantes seront exécutées sans problème. Pour éviter cela, .SHELLFLAGS = -ecil faut utiliser. Mais dans ce cas, vous ne pouvez plus utiliser le -préfixe (pour la commande personnelle du reçu) car la marque écrira que l'erreur est ignorée mais échouera tout le bloc. C'est donc || :la solution pour ignorer la commande. Mais ce n'est pas multiplateforme (Windows n'a pas || :ou || true)
Paul-AG

Réponses:

14

Chaque commande de mise à jour dans une règle Makefile est exécutée dans un shell séparé. Alors $? ne contient pas l'état de sortie de la commande précédente ayant échoué, il contient la valeur par défaut de $? dans une nouvelle coque. C'est pourquoi votre [$? -eq 0] le test réussit toujours.

Kyle Jones
la source
10

Vous n'avez pas besoin du test de $?puisque &&fonctionne si $?est nul et ||se poursuit en cas de valeur de retour non nulle.

Et vous n'avez pas besoin du moins car la valeur de retour à faire est prise lors du dernier appel de programme de la ligne. Donc ça marche bien

échec:

      @/bin/false && echo "success!" || echo "failure!" 

Succès:

      @/bin/true && echo "success!" || echo "failure!"

L'inverse se produit: si vous voulez faire votre propre message et que vous souhaitez quand même interrompre le processus de création avec une valeur non nulle, vous devez écrire quelque chose comme ceci:

échec:

      @/bin/false && echo "success!" || { echo "failure!"; exit 1; }
Andreas
la source
8

Depuis la documentation de GNU make :

Lorsque les erreurs doivent être ignorées, en raison de l'indicateur «-» ou «-i», make traite un retour d'erreur comme un succès , sauf qu'il affiche un message qui vous indique le code d'état avec lequel le shell est sorti, et indique que l'erreur a été ignorée.

Pour utiliser makel'état de sortie de dans un cas comme celui-ci, exécutez à makepartir d'un script:

#!/bin/bash
make
([ $? -eq 0 ] && echo "success!") || echo "failure!"

Et que votre Makefile contienne:

all:
    /bin/false
Timothy Martin
la source