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 0
status 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