Por que 0 é verdadeiro, mas falso é 1 no shell?


122
false; echo $?

O resultado acima será gerado 1, o que é contraditório com todas as outras linguagens de programação que conheço.

Alguma razão nisso?


3
Também está de acordo com o Unix Way ... não retorna nada em caso de sucesso.
Abdullah Jibaly

21
Porque um status de saída não é booleano. Simples assim.
Jens

2
Lembre-se de que falsenão é um booleano, como em outras linguagens de programação. É apenas um programa localizado em /bin/false( /usr/bin/falseem um Mac) que deve sempre retornar um código de saída de erro 1. Semelhante a true. Portanto, não existe elenco aqui. É apenas uma questão de códigos de saída.
Mikhail Vasin

Um shell é uma interface (do usuário) para um sistema operacional. Programas Unix etc. seguem a convenção de sair com 0 como OK, pelo motivo dado nas respostas (capacidade de comunicar mais de um motivo de falha). O shell simplesmente mantém essa convenção, o que torna as construções como if myprog; then echo OK; fifáceis e intuitivas. Caso contrário, você teria que inverter todos os testes para o sucesso de um programa!
Peter - Reintegração de Monica em

Respostas:


93

É uma convenção, mas particularmente útil quando você pensa a respeito. Em geral, se um programa for bem-sucedido, isso é tudo que você precisa saber. Se falhar, no entanto, você pode precisar saber todos os tipos de informações sobre a falha - por que aconteceu, como consertá-la, etc. Ter uma média zero de 'sucesso' e uma falha média diferente de zero permite que você possa verificar facilmente o sucesso e investigue o erro específico para obter mais detalhes, se desejar. Muitas APIs e estruturas têm uma convenção semelhante - as funções que tiveram êxito retornam 0 e aquelas que falharam retornaram um código de erro que descreve o caso de falha específico.


6
Eu entendo essa resposta, mas ainda não entendi por que a conversão de bool em int é invertida. Existe uma "convenção booleana de retorno" que diz: se você retornar verdadeiro, significa que não há erros, se você retornar falso, significa que ocorreu um erro (como a "convenção de código inteiro = erro" que é mais conhecida)?
Guillaume86 de

1
Presumir que haja uma conversão de booleano em int implica que você realmente não entendeu a resposta. Zero significa sucesso e todos de 1, 2, ..., 255 são códigos de erro que podem comunicar de forma útil diferentes cenários de falha. Um exemplo particular é o xargsque usa diferentes códigos de erro na faixa de 127 para indicar como um grupo de comandos falhou. A conversão que pode ser feita é int para bool, onde 0 mapeia para sucesso (que eu acho que você deseja expressar como verdadeiro / 1; mas perceba que esta é apenas outra convenção arbitrária) e todos os outros valores para falha.
tripleee

79

Bash é uma linguagem de programação (script), mas também é um shell e uma interface de usuário. Se 0fosse um erro, o programa poderia apresentar apenas um tipo de erro.

No entanto, no Bash, qualquer valor diferente de zero é um erro e podemos usar qualquer número de 1 a 255 para representar um erro. Isso significa que podemos ter muitos tipos diferentes de erros. 1é um erro geral, 126significa que um arquivo não pode ser executado, 127significa 'comando não encontrado', etc. Aqui está uma lista de códigos de saída Bash com significados especiais, mostrando alguns dos códigos de saída mais comuns.

Existem também muitos tipos de sucesso (o status de saída é 0). No entanto, um sucesso permitirá que você prossiga para a próxima etapa - você pode imprimir os resultados em uma tela ou executar um comando, etc.


8
Essa resposta pragmática, mostrando a utilidade de uma variedade de códigos de retorno, é preferível ao enfadonho de algumas das outras respostas.
javadba

1
E só para me divertir, vou apontar que as /usr/include/sysexits.hnotas saem de valores que talvez sejam mais ambiciosos, embora a convenção que representam seja da década de 1980. Essa convenção existe fora do escopo do bash.
ghoti

2
Uma explicação excelente, simples e pragmática. Isso precisa estar no topo.
Rey Leonard Amorato

3
"Se 0fosse um erro, o programa só poderia apresentar um tipo de erro" . Esta afirmação é a chave e a razão pela qual esta resposta deveria estar no topo.
rayryeng

1
@LuisLavaire Esse é um comportamento indefinido. Na prática, o número tende a ficar truncado; mas definitivamente não seria "mais errado" se o tempo de execução gerasse um aviso e / ou simplesmente travasse. O SO reservou exatamente um byte para esta informação e ao tentar colocar mais de um byte definitivamente há um erro. Mas os projetistas do SO provavelmente tiveram travamentos no primeiro dia, deram de ombros e decidiram que o melhor que podiam fazer é apenas truncar o valor e seguir em frente.
tripleee

30

Existem dois problemas relacionados aqui.

Primeiro, a pergunta do OP, por que 0 é verdadeiro, mas falso é 1 no shell? e a segunda, por que os aplicativos retornam 0 para sucesso e diferente de zero para falha?

Para responder à pergunta do OP, precisamos entender a segunda pergunta. As inúmeras respostas a esta postagem descrevem que se trata de uma convenção e listam algumas das sutilezas que esta convenção oferece. Algumas dessas sutilezas estão resumidas a seguir.

Por que os aplicativos retornam 0 para sucesso e diferente de zero para falha?

O código que invoca uma operação precisa saber duas coisas sobre o status de saída da operação. A operação foi encerrada com sucesso? [* 1] E se a operação não foi encerrada com sucesso, por que a operação foi encerrada sem sucesso? Qualquer valor pode ser usado para denotar sucesso. Mas 0 é mais conveniente do que qualquer outro número porque é portátil entre plataformas. Resumindo a resposta de xibo a esta pergunta em 16 de agosto de 2011:

Zero é independente de codificação.

Se quiséssemos armazenar um (1) em uma palavra inteira de 32 bits, a primeira pergunta seria "palavra big-endian ou palavra little endian?", Seguida por "quanto tempo os bytes estão compondo uma palavra little endian? ", enquanto o zero sempre terá a mesma aparência.

Também é necessário esperar que algumas pessoas usem errno para char ou short em algum ponto, ou mesmo para float. (int) ((char) ENOLCK) não é ENOLCK quando char não tem pelo menos 8 bits (máquinas ASCII de 7 bits são suportadas pelo UNIX), enquanto (int) ((char) 0) é 0 independente do detalhes arquitetônicos de char.

Uma vez que seja determinado que 0 será o valor de retorno para o sucesso, faz sentido usar qualquer valor diferente de zero para o fracasso. Isso permite que muitos códigos de saída respondam à pergunta por que a operação falhou.

Por que 0 é verdadeiro, mas falso é 1 no shell?

Um dos usos fundamentais dos shells é automatizar os processos fazendo o script deles. Normalmente, isso significa invocar uma operação e, em seguida, fazer outra coisa condicionalmente com base no status de saída da operação. Philippe A. explicou muito bem em sua resposta a esta postagem que

Em bash e em shells Unix em geral, os valores de retorno não são booleanos. Eles são códigos de saída inteiros.

É necessário então interpretar o status de saída dessas operações como um valor booleano. Faz sentido mapear um 0status de saída bem-sucedido ( ) para verdadeiro e qualquer status de saída diferente de zero / falha para falso. Isso permite a execução condicional de comandos shell encadeados.

Aqui está um exemplo mkdir deleteme && cd $_ && pwd. Como o shell interpreta 0 como verdadeiro, esse comando funciona convenientemente conforme o esperado. Se o shell interpretasse 0 como falso, você teria que inverter o status de saída interpretado para cada operação.

Em suma, seria absurdo para o shell interpretar 0 como falso, dada a convenção de que os aplicativos retornam 0 para um status de saída bem-sucedido.


[* 1]: Sim, muitas vezes as operações precisam retornar mais do que apenas uma simples mensagem de sucesso, mas isso está além do escopo deste tópico.

Veja também o Apêndice E no Advanced Bash-Scripting Guide


2
Olá, Só para enfatizar que, se o shell estivesse interpretando zero como falso e diferente de zero como verdadeiro, o truque de fazer mkdir deleteme && cd _$ && pwdrealmente não falharia; mas teríamos que substituí-lo por mkdir deleteme || cd _$ || pwd, o que na minha opinião é muito menos claro, porque o que realmente queremos fazer é mkdir deletemeecd _$epwd... (com “e” tendo aqui seu significado da linguagem comum).
Rémi Peyre

1
Acho que abordei isso na minha resposta. Para ser claro, quando você inverte a lógica, não é suficiente apenas substituir os &&operadores por ||operadores. Você precisaria aplicar totalmente a lei de De Morgan. Veja: en.wikipedia.org/wiki/De_Morgan%27s_laws
axiopisty

1
Outra consideração: vejo pelo menos três razões pelas quais, em uma base geral, seria natural afirmar que truecorresponde a zero e falsecorresponde a diferente de zero. Em primeiro lugar, se eu lhe disser algo, “ter contado a verdade” depende do número de mentiras que contei: ou é zero e eu disse (globalmente) a verdade ou é diferente de zero e menti. Isso é essencialmente o mesmo que querer que a lógica andcorresponda ao “e” comum da adição (ao restringir-se aos números naturais, obviamente).
Rémi Peyre

1
(continuação do comentário anterior). A segunda razão é que existe uma verdade, mas muitas não-verdades; portanto, é mais conveniente associar um único valor para truee todos os outros valores para false. (Isso é essencialmente o mesmo que as considerações sobre os valores de retorno dos programas).
Rémi Peyre

(continuação do comentário anterior). A terceira razão é que “verdadeiro que verdadeiro A” é o mesmo que “verdadeiro que A”, “falso que falso que A” é o mesmo que “falso que A”, etc .; de modo que truese comporta como o multiplicativo 1 e falsecomo o multiplicativo -1. O que, como potências de -1, significa que truese comporta adicionalmente como “par” e falsecomo “ímpar”. Mais uma vez, é o trueque merece ser zero ...
Rémi Peyre

17

O único ponto fundamental que acho importante entender é este. Em bash e em shells Unix em geral, os valores de retorno não são booleanos. Eles são códigos de saída inteiros. Como tal, você deve avaliá-los de acordo com a convenção, dizendo que 0 significa sucesso e outros valores significam algum erro.

Com test , [ ]ou [[ ]]operadores, condições Bash avaliar como verdadeiro no caso de um código de saída de 0 (o resultado de / bin / verdadeiro). Caso contrário, eles são avaliados como falsos.

Strings são avaliadas de forma diferente dos códigos de saída:

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

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

O (( ))operador aritmético interpreta 1 e 0 como verdadeiro e falso. Mas esse operador não pode ser usado como um substituto completo para test, [ ]ou [[ ]]. Aqui está um exemplo que mostra quando o operador aritmético é útil:

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

Obrigado pela explicação das chaves vs parênteses em verdadeiro / falso
javadba

15

É apenas uma convenção que um código de saída 0 significa sucesso. EXIT_SUCCESS será 0 em quase todos os sistemas modernos.

EDITAR:

"por que o teste 0 e o teste 1 retornam 0 (sucesso)?"

Essa é uma questão completamente diferente. A resposta é que passar um único argumento para teste sempre resulta em sucesso, a menos que esse argumento seja a string nula (""). Consulte a documentação do Open Group .


Então, por que test 0e test 1retorna 0 (sucesso)?
httpinterpret

5
@httpinterpret, testnão testa valores numéricos. Ele testa se a string é nula ou não. man testPara maiores informações.
Carl Norum

12

Normalmente os programas retornam zero para sucesso, diferente de zero para falha; falseretorna 1 porque é um valor diferente de zero conveniente, mas geralmente qualquer valor diferente de zero significa falha de algum tipo, e muitos programas retornarão valores diferentes de zero para indicar diferentes modos de falha


2
Votos negativos sem nenhuma explicação (ou razão óbvia) são uma merda e aqueles que o fazem são coxos porque não estão ajudando a comunidade em nada.
Abdullah Jibaly

1
Não tolerando isso ... mas são os 2 pontos deles ... e @MichaelMrozek .. com 19,6k, não pode doer muito, lol.
Alex Gray

1
@alexgray Isso foi há dois anos; na época eu tinha cerca de 5k. E eu estava mais curioso para saber o que havia de errado com a resposta do que sobre o representante
Michael Mrozek


4

é uma convenção que data dos primeiros dias do Unix.

Por convenção, todas as chamadas de sistema retornam 0 se forem bem-sucedidas, diferente de zero caso contrário, porque então diferentes números podem ser usados ​​para indicar diferentes motivos de falha.

Os shells seguem esta convenção, 0 significa o último comando bem-sucedido, diferente de zero caso contrário. Da mesma forma, o valor de retorno diferente de zero é útil para mensagens de erro de saída: por exemplo, 1: "morte cerebral", 2: "sem coração" e assim por diante.


Esta é a resposta.
Peter - Reintegrar Monica

3

Você está tentando igualar verdadeiro / falso com sucesso / fracasso.

São duas dicotomias completamente, embora sutilmente a princípio, diferentes!

No script de shell, não existe verdadeiro / falso. As 'expressões' do shell não são interpretadas como verdadeiro / falso. Em vez disso, 'expressões' de shell são processos que têm sucesso ou falham.

Obviamente, um processo pode falhar por vários motivos. Portanto, precisamos de um conjunto maior de códigos para mapear possíveis falhas. Os inteiros positivos fazem o truque. Por outro lado, se o processo for bem-sucedido, isso significa que fez exatamente o que deveria. Como só existe uma maneira de fazer isso, precisamos apenas de um código. 0 resolve o problema.

Em C, estamos criando um programa. Em um script de shell, estamos executando vários programas para fazer algo.

Diferença!


É confuso. Digite 'man ['. Ele mostra O utilitário de teste avalia a expressão e, se for verdadeiro, retorna um status de saída zero (verdadeiro); caso contrário, retorna 1 (falso). Se não houver expressão, o teste também retorna 1 (falso).
cavalgada em

1

Talvez uma boa maneira de lembrar isso seja:

  • Os códigos de retorno respondem "qual é a resposta?" verdadeiro (ou outro resultado) ou falso
  • Os códigos de saída respondem "qual é o problema?" código de saída ou sem problema (0)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.