Perché 0 è vero ma falso è 1 nella shell?


122
false; echo $?

Quanto sopra verrà prodotto 1, il che è contraddittorio con tutti gli altri linguaggi di programmazione che conosco.

Qualche motivo in questo?


3
È anche in linea con Unix Way ... non restituire nulla in caso di successo.
Abdullah Jibaly

21
Perché uno stato di uscita non è un booleano. Semplice come quella.
Jens

2
Tieni presente che falsenon è un booleano, come in altri linguaggi di programmazione. È solo un programma situato in /bin/false( /usr/bin/falsesu un Mac) che ha lo scopo di restituire sempre un codice di uscita di errore 1. Simile per true. Quindi non c'è niente come il casting qui. Si tratta solo di codici di uscita.
Mikhail Vasin

Una shell è un'interfaccia (utente) per un sistema operativo. I programmi Unix ecc. Seguono la convenzione di uscire con 0 come OK, per il motivo fornito nelle risposte (capacità di comunicare più di un motivo di errore). La shell mantiene semplicemente questa convenzione, il che rende i costrutti if myprog; then echo OK; fifacili e intuitivi. Altrimenti dovresti invertire ogni test per il successo di un programma!
Peter - Ripristina Monica il

Risposte:


93

È una convenzione, ma particolarmente utile se ci pensi. In generale, se un programma ha successo, è tutto ciò che devi sapere. Se fallisce, tuttavia, potrebbe essere necessario conoscere tutti i tipi di informazioni sull'errore: perché si è verificato, come risolverlo, ecc. Avere zero "successo" medio e un errore medio diverso da zero ti consente di controllare abbastanza facilmente il successo e, se lo desideri, esamina l'errore specifico per ulteriori dettagli. Molte API e framework hanno una convenzione simile: le funzioni che hanno esito positivo restituiscono 0 e quelle che non riescono restituiscono un codice di errore che descrive il particolare caso di errore.


6
Capisco questa risposta, ma ancora non capisco perché la conversione da bool a int sia invertita. Esiste una "convenzione booleana di ritorno" che dice: se restituisci vero, significa che non ci sono errori, se restituisci falso, significa che si è verificato un errore (come la "convenzione del codice di errore intero =" che è più nota)?
Guillaume86

1
Supponendo che ci sia una conversione da booleano a int implica che non hai davvero capito la risposta. Zero significa successo e tutti 1, 2, ..., 255 sono codici di errore che possono comunicare utilmente diversi scenari di errore. Un esempio particolare è xargsche utilizza diversi codici di errore nell'intervallo intorno a 127 per indicare come un gruppo di comandi ha fallito. La conversione che può essere eseguita è int to bool dove 0 mappato al successo (che immagino tu voglia esprimere come vero / 1; ma renditi conto che questa è solo un'altra convenzione arbitraria) e tutti gli altri valori al fallimento.
tripleee

79

Bash è un linguaggio di programmazione (scripting), ma è anche una shell e un'interfaccia utente. Se0 era un errore, il programma potrebbe presentare solo un tipo di errore.

Tuttavia in Bash, qualsiasi valore diverso da zero è un errore e possiamo utilizzare qualsiasi numero compreso tra 1 e 255 per rappresentare un errore. Ciò significa che possiamo avere molti diversi tipi di errori. 1è un errore generale, 126significa che un file non può essere eseguito, 127significa "comando non trovato", ecc. Ecco un elenco di codici di uscita Bash con significati speciali che mostrano alcuni dei codici di uscita più comuni.

Esistono anche molti tipi di successo (lo stato di uscita è 0). Tuttavia, un successo ti consentirà di procedere al passaggio successivo: puoi ad esempio stampare i risultati su una schermata o eseguire un comando, ecc.


8
Questa risposta pragmatica che mostra l'utilità di una varietà di codici di ritorno è preferibile alla predicazione di alcune delle altre risposte.
javadba

1
E solo per il gusto di farlo, faccio notare che /usr/include/sysexits.hnota valori di uscita forse più ambiziosi, anche se la convenzione che rappresentano risale agli anni '80. Questa convenzione esiste al di fuori dell'ambito di bash.
ghoti

2
Una spiegazione fantastica, semplice e pragmatica. Questo deve essere al top.
Rey Leonard Amorato

3
"Se 0era un errore, il programma potrebbe presentare solo un tipo di errore" . Questa affermazione è fondamentale e il motivo per cui questa risposta dovrebbe essere in cima.
rayryeng

1
@LuisLavaire Questo è un comportamento indefinito. In pratica, il numero tende ad essere troncato; ma sicuramente non sarebbe "più sbagliato" se il runtime generasse un avviso e / o semplicemente andasse in crash. Il sistema operativo ha riservato esattamente un byte per queste informazioni e il tentativo di inserire più di un byte è sicuramente un errore. Ma i progettisti del sistema operativo probabilmente sono incappati in arresti anomali il primo giorno e hanno alzato le spalle e hanno deciso che il meglio che possono fare è semplicemente troncare il valore e andare avanti.
tripleee

30

Ci sono due problemi correlati qui.

In primo luogo, la domanda dell'OP, perché 0 è vero ma falso è 1 nella shell? e il secondo, perché le applicazioni restituiscono 0 per il successo e diverso da zero per il fallimento?

Per rispondere alla domanda del PO dobbiamo capire la seconda domanda. Le numerose risposte a questo post hanno descritto che questa è una convenzione e hanno elencato alcune delle sottigliezze che questa convenzione offre. Alcune di queste sottigliezze sono riassunte di seguito.

Perché le applicazioni restituiscono 0 per il successo e diverso da zero per il fallimento?

Il codice che richiama un'operazione deve conoscere due cose sullo stato di uscita dell'operazione. L'operazione è terminata con successo? [* 1] E se l'operazione non è terminata correttamente, perché l'operazione è terminata senza successo? Qualsiasi valore potrebbe essere utilizzato per indicare il successo. Ma 0 è più conveniente di qualsiasi altro numero perché è portabile tra le piattaforme. Riassumendo la risposta di xibo a questa domanda il 16 agosto 2011:

Zero è indipendente dalla codifica.

Se volessimo memorizzare uno (1) in una parola intera a 32 bit, la prima domanda sarebbe "parola big-endian o parola little-endian?", Seguita da "quanto tempo sono i byte che compongono una parola little-endian?" ", mentre lo zero sarà sempre lo stesso.

Inoltre ci si deve aspettare che alcune persone lanciano errno a char o short ad un certo punto, o anche a float. (int) ((char) ENOLCK) non è ENOLCK quando char non è lungo almeno 8 bit (le macchine con caratteri ASCII a 7 bit sono supportate da UNIX), mentre (int) ((char) 0) è 0 indipendente dal dettagli architettonici di char.

Una volta stabilito che 0 sarà il valore restituito per il successo, ha senso utilizzare qualsiasi valore diverso da zero per il fallimento. Ciò consente a molti codici di uscita di rispondere alla domanda sul perché l'operazione non è riuscita.

Perché 0 è vero ma falso è 1 nella shell?

Uno degli usi fondamentali delle shell è automatizzare i processi tramite script. Di solito questo significa invocare un'operazione e quindi fare qualcos'altro in base allo stato di uscita dell'operazione. Philippe A. ha spiegato bene nella sua risposta a questo post che

In bash e nelle shell unix in generale, i valori restituiti non sono booleani. Sono codici di uscita interi.

È quindi necessario interpretare lo stato di uscita di queste operazioni come un valore booleano. Ha senso mappare uno 0stato di uscita riuscito ( ) a vero e qualsiasi stato di uscita diverso da zero / errore a falso. Ciò consente l'esecuzione condizionale dei comandi di shell concatenati.

Ecco un esempio mkdir deleteme && cd $_ && pwd. Poiché la shell interpreta 0 come vero, questo comando funziona convenientemente come previsto. Se la shell dovesse interpretare 0 come falso, dovresti invertire lo stato di uscita interpretato per ogni operazione.

In breve, non avrebbe senso che la shell interpretasse 0 come falso, data la convenzione secondo cui le applicazioni restituiscono 0 per uno stato di uscita riuscito.


[* 1]: Sì, molte volte le operazioni devono restituire più di un semplice messaggio di successo, ma questo va oltre lo scopo di questo thread.

Vedi anche l' Appendice E nella Advanced Bash-Scripting Guide


2
Ciao, Giusto per sottolineare che, se la shell interpretasse zero come falso e diverso da zero come vero, il trucco del fare mkdir deleteme && cd _$ && pwdnon fallirebbe davvero; ma dovremmo sostituirlo con mkdir deleteme || cd _$ || pwd, il che a mio parere è molto meno chiaro, perché ciò che in realtà vogliamo fare è mkdir deleteme" e " cd _$" e " pwd... (con "e" che ha qui il suo significato dal linguaggio ordinario).
Rémi Peyre

1
Penso di averlo spiegato nella mia risposta. Per essere chiari, però, quando si inverte la logica non è sufficiente sostituire semplicemente gli &&operatori con ||operatori. Dovresti applicare completamente la legge di De Morgan. Vedi: en.wikipedia.org/wiki/De_Morgan%27s_laws
axiopisty

1
Un'altra considerazione: vedo almeno tre ragioni per le quali, in generale, sarebbe naturale affermare che truecorrisponde a zero e falsecorrisponde a diverso da zero. Primo, se ti dico una cosa, "l'averti detto la verità" dipende dal numero di bugie che ho detto: o è zero e ho detto (globalmente) la verità, oppure è diverso da zero e ho mentito. Questo è essenzialmente lo stesso di volere che la logica andcorrisponda al comune "e" dell'addizione (quando ci si limita ai numeri naturali, ovviamente).
Rémi Peyre

1
(continua dal commento precedente). La seconda ragione è che c'è una verità ma molte non verità; quindi, è più conveniente associare un unico valore per truee tutti gli altri valori per false. (Questo è essenzialmente lo stesso delle considerazioni sui valori di ritorno dei programmi).
Rémi Peyre

(continua dal commento precedente). La terza ragione è che "vero quel vero che A" è lo stesso di "vero quel A", "falso quel falso che A" è lo stesso di "falso quello A", ecc .; così che truesi comporta come il moltiplicativo 1 e falsecome il moltiplicativo -1. Il che, come potenze di -1, significa che truesi comporta in aggiunta come "pari" e falsecome "dispari". Di nuovo, lo ètrue che merita di essere zero ...
Rémi Peyre

17

L'unico punto fondamentale che trovo importante da capire è questo. In bash e nelle shell unix in generale, i valori restituiti non sono booleani. Sono codici di uscita interi. Pertanto, è necessario valutarli secondo la convenzione dicendo che 0 significa successo e altri valori indicano un errore.

Con test, [ ]o [[ ]]gli operatori, le condizioni di bash valutano come vero nel caso di un codice di uscita pari a 0 (il risultato di / bin / true). Altrimenti valutano come false.

Le stringhe vengono valutate in modo diverso rispetto ai codici di uscita:

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

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

L' (( ))operatore aritmetico interpreta 1 e 0 come vero e falso. Ma quell'operatore non può essere utilizzato come sostituto completo di test,[ ] o [[ ]]. Ecco un esempio che mostra quando l'operatore aritmetico è utile:

for (( counter = 0 ; counter < 10 ; counter ++ )) ; do
  if (( counter % 2 )) ; then echo "odd number $counter" ; fi
done

Grazie per la spiegazione delle parentesi graffe vs parentesi su vero / falso
javadba

15

È solo una convenzione che un codice di uscita 0 significhi successo. EXIT_SUCCESS sarà 0 su quasi tutti i sistemi moderni.

MODIFICARE:

"perché sia ​​il test 0 che il test 1 restituiscono 0 (successo)?"

Questa è una domanda completamente diversa. La risposta è che il passaggio di un singolo argomento per il test ha sempre esito positivo a meno che tale argomento non sia la stringa nulla (""). Vedere la documentazione di Open Group .


Allora perché sia test 0e test 1restituisce 0 (successo)?
httpinterpretare

5
@httpinterpret, testnon verifica i valori numerici. Verifica se quella stringa è o meno la stringa nulla. man testper maggiori informazioni.
Carl Norum

12

In genere i programmi restituiscono zero in caso di successo, diverso da zero in caso di fallimento; falserestituisce 1 perchéèun valore diverso da zero conveniente, ma generalmente qualsiasi valore diverso da zero significa un errore di qualche tipo e molti programmi restituiranno valori diversi da zero per indicare diverse modalità di errore


2
I voti negativi senza alcuna spiegazione (o ragione ovvia) fanno schifo e quelli che lo fanno sono zoppi perché non aiutano affatto la comunità.
Abdullah Jibaly

1
Non perdonandolo ... ma sono i loro 2 punti ... e @MichaelMrozek .. con 19.6k, non può far male, lol.
Alex Grey

1
@alexgray Questo è stato due anni fa; all'epoca avevo circa 5k. Ed ero più curioso di sapere cosa c'era di sbagliato nella risposta che sul rappresentante
Michael Mrozek


4

è una convenzione che risale ai primi giorni di Unix.

Per convenzione, tutte le chiamate di sistema restituiscono 0 in caso di esito positivo, diverso da zero in caso contrario, poiché è possibile utilizzare numeri diversi per indicare diversi motivi del fallimento.

I gusci seguono questa convenzione, 0 significa che l'ultimo comando è riuscito, diversamente da zero. Allo stesso modo, il valore di ritorno diverso da zero è utile per i messaggi di errore di output: ad esempio 1: "cervello morto", 2: "senza cuore" e così via.


Questa è la risposta.
Peter - Ripristina Monica il

3

Stai cercando di equiparare vero / falso con successo / fallimento.

Sono due dicotomie completamente, anche se sottilmente all'inizio, diverse!

Nello scripting di shell non esiste il vero / falso. Le "espressioni" della shell non vengono interpretate come vero / falso. Piuttosto, le "espressioni" di shell sono processi che riescono o falliscono.

Ovviamente, un processo potrebbe fallire per molte ragioni. Quindi abbiamo bisogno di un set di codici più ampio per mappare i possibili errori. Gli interi positivi fanno il trucco. D'altra parte, se il processo ha successo, significa che ha fatto esattamente quello che doveva fare. Poiché esiste un solo modo per farlo, è necessario un solo codice. 0 fa il trucco.

In C, stiamo creando un programma. In uno script di shell, stiamo eseguendo una serie di programmi per ottenere qualcosa.

Differenza!


È confusionario. Digita "man [". Mostra L'utilità di test valuta l'espressione e, se restituisce true, restituisce uno stato di uscita zero (vero); altrimenti restituisce 1 (falso). Se non è presente alcuna espressione, test restituisce anche 1 (falso).
cavalcata

1

Forse un buon modo per ricordarlo è:

  • Codici di ritorno risposta "qual è la risposta?" vero (o altro risultato) o falso
  • I codici di uscita rispondono "qual è il problema?" codice di uscita o nessun problema (0)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.