Pourquoi 0 est vrai mais faux est 1 dans le shell?

122
false; echo $?

Ce qui précède produira 1, ce qui est contradictoire avec tous les autres langages de programmation que je connais.

Une raison à cela?

httpinterpret
la source
3
C'est aussi conforme à la méthode Unix ... ne retournez rien en cas de succès.
Abdullah Jibaly
21
Parce qu'un statut de sortie n'est pas un booléen. Aussi simple que cela.
Jens
2
Gardez à l'esprit que ce falsen'est pas un booléen, comme dans d'autres langages de programmation. C'est juste un programme situé à /bin/false( /usr/bin/falsesur un Mac) qui est censé toujours renvoyer un code de sortie d'erreur 1. Similaire pour true. Il n'y a donc pas de casting ici. Tout est question de codes de sortie.
Mikhail Vasin
Un shell est une interface (utilisateur) vers un système d'exploitation. Les programmes Unix etc. suivent la convention de sortie avec 0 comme OK, pour la raison donnée dans les réponses (capacité à communiquer plus d'une raison d'échec). Le shell conserve simplement cette convention, ce qui rend les constructions if myprog; then echo OK; fifaciles et intuitives. Sinon, vous devrez inverser chaque test pour réussir un programme!
Peter - Réintègre Monica

Réponses:

93

C'est une convention, mais particulièrement utile quand on y pense. En général, si un programme réussit, c'est tout ce que vous devez savoir. En cas d'échec, cependant, vous devrez peut-être connaître toutes sortes d'informations sur l'échec - pourquoi cela s'est produit, comment le réparer, etc. et recherchez l'erreur particulière pour plus de détails si vous le souhaitez. De nombreuses API et frameworks ont une convention similaire - les fonctions qui réussissent renvoient 0 et celles qui échouent renvoient un code d'erreur décrivant le cas d'échec particulier.

Carl Norum
la source
6
Je comprends cette réponse, mais je ne comprends toujours pas pourquoi la conversion booléenne en int est inversée. Existe-t-il une "convention booléenne de retour" qui dit: si vous retournez true, cela signifie qu'il n'y a pas d'erreur, si vous retournez false, cela signifie qu'une erreur s'est produite (comme la "convention d'entier = code d'erreur" qui est plus connue)?
Guillaume86
1
En supposant qu'il y ait une conversion booléenne en int, cela signifie que vous n'avez pas vraiment compris la réponse. Zéro signifie succès et tous les 1, 2, ..., 255 sont des codes d'erreur qui peuvent utilement communiquer différents scénarios de défaillance. Un exemple particulier est celui xargsqui utilise différents codes d'erreur dans la plage d'environ 127 pour indiquer comment un groupe de commandes a échoué. La conversion qui peut être effectuée est int en bool où 0 correspond au succès (ce que je suppose que vous voulez exprimer comme vrai / 1; mais sachez que ce n'est qu'une autre convention arbitraire) et toutes les autres valeurs à l'échec.
tripleee
79

Bash est un langage de programmation (scripting), mais c'est aussi un shell et une interface utilisateur. Si 0c'était une erreur, alors le programme ne pouvait présenter qu'un seul type d'erreur.

Cependant, dans Bash, toute valeur différente de zéro est une erreur, et nous pouvons utiliser n'importe quel nombre de 1 à 255 pour représenter une erreur. Cela signifie que nous pouvons avoir de nombreux types d'erreurs. 1est une erreur générale, 126signifie qu'un fichier ne peut pas être exécuté, 127signifie «commande introuvable», etc. Voici une liste de codes de sortie Bash avec des significations spéciales montrant certains des codes de sortie les plus courants.

Il existe également de nombreux types de succès (le statut de sortie est 0). Cependant, un succès vous permettra de passer à l'étape suivante: vous pouvez aimer imprimer les résultats sur un écran, ou exécuter une commande, etc.

Stefan Lasiewski
la source
8
Cette réponse pragmatique montrant l'utilité d'une variété de codes de retour est préférable à la prédication de certaines des autres réponses.
javadba
1
Et juste pour le plaisir, je ferai remarquer que des /usr/include/sysexits.hvaleurs de sortie sont peut-être plus ambitieuses, même si la convention qu'elles représentent remonte aux années 1980. Cette convention existe en dehors du champ d'application de bash.
ghoti
2
Une excellente explication, simple et pragmatique. Cela doit être au sommet.
Rey Leonard Amorato
3
"Si 0c'était une erreur, alors le programme ne pouvait présenter qu'un seul type d'erreur" . Cette déclaration est la clé et la raison pour laquelle cette réponse devrait être en haut.
rayryeng
1
@LuisLavaire C'est un comportement indéfini. En pratique, le nombre a tendance à être tronqué; mais ce ne serait certainement pas "plus faux" si le runtime générait un avertissement et / ou tombait simplement en panne. Le système d'exploitation a réservé exactement un octet pour ces informations et en essayant de mettre plus d'un octet, il y a définitivement une erreur. Mais les concepteurs du système d'exploitation ont probablement rencontré des plantages le premier jour, ont haussé les épaules et ont décidé que le mieux qu'ils pouvaient faire était de simplement tronquer la valeur et de passer à autre chose.
tripleee
30

Il y a deux problèmes liés ici.

Tout d'abord, la question du PO, pourquoi 0 est vrai mais faux est 1 dans le shell? et le second, pourquoi les applications renvoient-elles 0 pour succès et non nul pour échec?

Pour répondre à la question du PO, nous devons comprendre la deuxième question. Les nombreuses réponses à ce post ont décrit qu'il s'agit d'une convention et ont énuméré certaines des subtilités de cette convention. Certaines de ces subtilités sont résumées ci-dessous.

Pourquoi les applications renvoient-elles 0 en cas de succès et non nul en cas d'échec?

Le code qui appelle une opération doit connaître deux choses sur l'état de sortie de l'opération. L'opération s'est-elle terminée avec succès? [* 1] Et si l'opération ne se termine pas correctement, pourquoi l'opération s'est-elle terminée sans succès? N'importe quelle valeur peut être utilisée pour indiquer le succès. Mais 0 est plus pratique que tout autre nombre car il est portable entre les plates-formes. Résumant la réponse de xibo à cette question le 16 août 2011:

Le zéro est indépendant du codage.

Si nous voulions stocker un (1) dans un mot entier de 32 bits, la première question serait "mot big-endian ou petit-endian?", Suivi de "combien de temps les octets composent-ils un mot little-endian?" ", tandis que zéro aura toujours le même aspect.

De plus, il faut s'attendre à ce que certaines personnes lancent errno en char ou short à un moment donné, ou même en flottant. (int) ((char) ENOLCK) n'est pas ENOLCK lorsque char n'a pas au moins 8 bits de long (les machines à caractères ASCII 7 bits sont prises en charge par UNIX), tandis que (int) ((char) 0) vaut 0 indépendamment de détails architecturaux du char.

Une fois qu'il est déterminé que 0 sera la valeur de retour pour le succès, il est logique d'utiliser une valeur non nulle en cas d'échec. Cela permet à de nombreux codes de sortie de répondre à la question de savoir pourquoi l'opération a échoué.

Pourquoi 0 est vrai mais faux est 1 dans le shell?

L'un des usages fondamentaux des shells est d'automatiser les processus en les scriptant. Habituellement, cela signifie appeler une opération, puis faire autre chose de manière conditionnelle en fonction de l'état de sortie de l'opération. Philippe A. a bien expliqué dans sa réponse à ce post que

Dans bash et dans les shells Unix en général, les valeurs de retour ne sont pas booléennes. Ce sont des codes de sortie entiers.

Il est alors nécessaire d'interpréter l'état de sortie de ces opérations comme une valeur booléenne. Il est logique de mapper un 0état de sortie réussi ( ) sur true et tout état de sortie non nul / échec sur false. Cela permet l'exécution conditionnelle de commandes shell chaînées.

Voici un exemple mkdir deleteme && cd $_ && pwd. Comme le shell interprète 0 comme vrai, cette commande fonctionne comme prévu. Si le shell devait interpréter 0 comme faux, vous devrez inverser l'état de sortie interprété pour chaque opération.

En bref, il serait absurde pour le shell d'interpréter 0 comme faux étant donné la convention selon laquelle les applications renvoient 0 pour un statut de sortie réussi.


[* 1]: Oui, de nombreuses fois, les opérations doivent renvoyer plus qu'un simple message de réussite, mais cela dépasse la portée de ce thread.

Voir également l' annexe E dans le Guide de scripts Bash avancés

axiopistie
la source
2
Bonjour, Juste pour souligner que, si le shell interprétait zéro comme faux et non-zéro comme vrai, l'astuce de faire mkdir deleteme && cd _$ && pwdn'échouerait pas vraiment; mais il faudrait le remplacer par mkdir deleteme || cd _$ || pwd, ce qui à mon avis est beaucoup moins clair, car ce que nous voulons réellement faire est mkdir deleteme« et » cd _$« et » pwd... (par «et» ayant ici son sens du langage ordinaire).
Rémi Peyre
1
Je pense que j'ai couvert cela dans ma réponse. Pour être clair cependant, lorsque vous inversez la logique, il ne suffit pas de remplacer les &&opérateurs par des ||opérateurs. Vous auriez besoin d'appliquer pleinement la loi de De Morgan. Voir: en.wikipedia.org/wiki/De_Morgan%27s_laws
axiopisty
1
Autre considération: je vois au moins trois raisons pour lesquelles, d'une manière générale, il serait naturel de dire qui truecorrespond à zéro et falsecorrespond à non nul. Premièrement, si je vous dis quelque chose, «vous avoir dit la vérité» dépend du nombre de mensonges que j'ai dit: soit c'est zéro et j'ai dit (globalement) la vérité, soit c'est différent de zéro et j'ai menti. C'est essentiellement la même chose de vouloir que le logique andcorresponde au «et» commun de l'addition (en se limitant aux nombres naturels, évidemment).
Rémi Peyre
1
(suite du commentaire précédent). La deuxième raison est qu'il y a une vérité mais de nombreuses non-vérités; il est donc plus pratique d'associer une valeur unique pour trueet toutes les autres valeurs pour false. (Ceci est essentiellement le même que les considérations sur les valeurs de retour des programmes).
Rémi Peyre
(suite du commentaire précédent). La troisième raison est que «vrai que vrai que A» est le même que «vrai que A», «faux que faux que A» est le même que «faux que A», etc. de sorte que truese comporte comme le multiplicatif 1 et falsecomme le multiplicatif -1. Ce qui, en tant que puissances de -1, signifie qu'il truese comporte en plus comme "pair" et falsecomme "impair". Encore une fois, c'est truece qui mérite d'être nul ...
Rémi Peyre
17

Le seul point fondamental que je trouve important de comprendre est le suivant. Dans bash et dans les shells Unix en général, les valeurs de retour ne sont pas booléennes. Ce sont des codes de sortie entiers. En tant que tel, vous devez les évaluer selon la convention en disant que 0 signifie le succès, et d'autres valeurs signifient une erreur.

Avec test, [ ]ou les [[ ]]opérateurs, les conditions de bash évaluent comme vrai dans le cas d'un code de sortie de 0 (le résultat de / bin / true). Sinon, ils évaluent comme faux.

Les chaînes sont évaluées différemment des codes de sortie:

if [ 0 ] ; then echo not null ; fi
if [ $(echo 0) ] ; then echo not null ; fi

if [ -z "" ] ; then echo null ; fi

L' (( ))opérateur arithmétique interprète 1 et 0 comme vrai et faux. Mais cet opérateur ne peut pas être utilisé comme un remplacement complet pour test, [ ]ou [[ ]]. Voici un exemple montrant quand l'opérateur arithmétique est utile:

for (( counter = 0 ; counter < 10 ; counter ++ )) ; do
  if (( counter % 2 )) ; then echo "odd number $counter" ; fi
done
Philippe A.
la source
Merci pour l'explication des accolades vs parenthèses sur vrai / faux
javadba
15

C'est juste une convention selon laquelle un code de sortie 0 signifie le succès. EXIT_SUCCESS sera égal à 0 sur presque tous les systèmes modernes.

ÉDITER:

"Pourquoi le test 0 et le test 1 retournent-ils 0 (succès)?"

C'est une question complètement différente. La réponse est que passer un seul argument au test aboutit toujours à un succès sauf si cet argument est la chaîne nulle (""). Consultez la documentation Open Group .

Matthew Flaschen
la source
Alors pourquoi les deux test 0et test 1renvoie 0 (succès)?
httpinterpret
5
@httpinterpret, testne teste pas les valeurs numériques. Il teste si cette chaîne est ou non la chaîne nulle. man testpour plus d'informations.
Carl Norum
12

En règle générale, les programmes renvoient zéro en cas de succès, différent de zéro en cas d'échec; falserenvoie 1 parce que c'est une valeur non nulle pratique, mais généralement toute valeur non nulle signifie un échec d'une certaine sorte, et de nombreux programmes renverront différentes valeurs non nulles pour indiquer différents modes d'échec

Michael Mrozek
la source
2
Les votes négatifs sans explication (ou raison évidente) sont nulles et ceux qui le font sont boiteux parce qu'ils n'aident pas du tout la communauté.
Abdullah Jibaly
1
Je ne le tolère pas ... mais ce sont leurs 2 points ... et @MichaelMrozek ... avec 19,6k, ça ne peut pas faire trop mal, lol.
Alex Gray
1
@alexgray C'était il y a deux ans; à l'époque, j'avais environ 5k. Et j'étais plus curieux de savoir ce qui n'allait pas avec la réponse que dans le représentant
Michael Mrozek
4

c'est une convention datant des débuts d'Unix.

Par convention, tous les appels système renvoient 0 en cas de succès, non nul dans le cas contraire, car des nombres différents peuvent alors être utilisés pour indiquer une raison différente de l'échec.

Les shells suivent cette convention, 0 signifie que la dernière commande a réussi, différent de zéro dans le cas contraire. De même, la valeur de retour différente de zéro est pratique pour les messages d'erreur de sortie: par exemple 1: "brain dead", 2: "heartless", et ainsi de suite.


la source
Voilà la réponse.
Peter - Réintègre Monica
3

Votre tentative d'assimiler vrai / faux avec succès / échec.

Ce sont deux dichotomies complètement, quoique subtilement au début!

Dans les scripts shell, il n'y a pas de vrai / faux. Les «expressions» du shell ne sont pas interprétées comme vrai / faux. Les «expressions» du shell sont plutôt des processus qui réussissent ou échouent.

De toute évidence, un processus peut échouer pour de nombreuses raisons. Nous avons donc besoin d'un plus grand ensemble de codes pour mapper les pannes possibles. Les entiers positifs font l'affaire. D'un autre côté, si le processus réussit, cela signifie qu'il a fait exactement ce qu'il était censé faire. Puisqu'il n'y a qu'une seule façon de faire cela, nous n'avons besoin que d'un seul code. 0 fait l'affaire.

En C, nous créons un programme. Dans un script shell, nous exécutons un tas de programmes pour faire quelque chose.

Différence!

capitainerie
la source
C'est confu. Tapez 'homme ['. Il indique que l'utilitaire de test évalue l'expression et, s'il prend la valeur true, renvoie un statut de sortie nul (vrai); sinon, il renvoie 1 (faux). S'il n'y a pas d'expression, test renvoie également 1 (faux).
cavalcade
1

Peut-être qu'un bon moyen de s'en souvenir est:

  • Les codes de retour répondent "quelle est la réponse?" vrai (ou autre résultat) ou faux
  • Les codes de sortie répondent "quel est le problème?" code de sortie ou pas de problème (0)
jjisnow
la source