开发者

Why 0 is true but false is 1 in the shell?

开发者 https://www.devze.com 2023-01-02 01:29 出处:网络
false; echo $? The above will output 1, which is c开发者_开发百科ontradictory with all other programming languages I know.
false; echo $?

The above will output 1, which is c开发者_开发百科ontradictory with all other programming languages I know.

Any reason in this?


It's a convention, but a particularly useful one when you think about it. In general, if a program succeeds that's all you need to know. If it fails, however, you might need to know all kinds of information about the failure - why it happened, how to fix it, etc. Having zero mean 'success' and non-zero mean failure lets you can check pretty easily for success, and investigate the particular error for more details if you want to. A lot of APIs and frameworks have a similar convention - functions that succeed return 0 and and those that fail give back an error code describing the particular failure case.


Bash is a programming (scripting) language, but it's also a shell and a user-interface. If 0 was error, then the program could only present one kind of error.

However in Bash, any nonzero value is an error, and we may use any number from 1-255 to represent an error. This means we can have many different kinds of errors. 1 is a general error, 126 means that a file cannot be executed, 127 means 'command not found', etc. Here's a list of Bash Exit Codes With Special Meanings showing some of the most common exit codes.

There are also many kinds of success (exit status is 0). However, a success will allow you to proceed to the next step—you can like print results to a screen, or execute a command, etc.


There are two related issues here.

First, the OP's question, Why 0 is true but false is 1 in the shell? and the second, why do applications return 0 for success and non-zero for failure?

To answer the OP's question we need to understand the second question. The numerous answers to this post have described that this is a convention and have listed some of the niceties this convention affords. Some of these niceties are summarized below.

Why do applications return 0 for success and non-zero for failure?

Code that invokes an operation needs to know two things about the exit status of the operation. Did the operation exit successfully? [*1] And if the operation does not exit successfully why did the operation exit unsuccessfully? Any value could be used to denote success. But 0 is more convenient than any other number because it is portable between platforms. Summarizing xibo's answer to this question on 16 Aug 2011:

Zero is encoding-independent.

If we wanted to store one(1) in a 32-bit integer word, the first question would be "big-endian word or little-endian word?", followed by "how long are the bytes composing a little-endian word?", while zero will always look the same.

Also it needs to be expected that some people cast errno to char or short at some point, or even to float. (int)((char)ENOLCK) is not ENOLCK when char is not at least 8-bit long (7-bit ASCII char machines are supported by UNIX), while (int)((char)0) is 0 independent of the architectural details of char.

Once it is determined that 0 will be the return value for success, then it makes sense to use any non-zero value for failure. This allows many exit codes to answer the question why the operation failed.

Why 0 is true but false is 1 in the shell?

One of the fundamental usages of shells is to automate processes by scripting them. Usually this means invoking an operation and then doing something else conditionally based on the exit status of the operation. Philippe A. explained nicely in his answer to this post that

In bash and in unix shells in general, return values are not boolean. They are integer exit codes.

It's necessary then to interpret the exit status of these operations as a boolean value. It makes sense to map a successful (0) exit status to true and any non-zero/failure exit status to false. Doing this allows conditional execution of chained shell commands.

Here is an example mkdir deleteme && cd $_ && pwd. Because the shell interprets 0 as true this command conveniently works as expected. If the shell were to interpret 0 as false then you'd have to invert the interpreted exit status for each operation.

In short, it would be nonsensical for the shell to interpret 0 as false given the convention that applications return 0 for a successful exit status.


[*1]: Yes, many times operations need to return more than just a simple success message but that is beyond the scope of this thread.

See also Appendix E in the Advanced Bash-Scripting Guide


The one fundamental point I find important to understand is this. In bash and in unix shells in general, return values are not boolean. They are integer exit codes. As such, you must evaluate them according to the convention saying 0 means success, and other values mean some error.

With test, [ ] or [[ ]] operators, bash conditions evaluate as true in case of an exit code of 0 (the result of /bin/true). Otherwise they evaluate as false.

Strings are evaluated differently than exit codes:

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

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

The (( )) arithmetic operator interprets 1 and 0 as true and false. But that operator cannot be used as a complete replacement for test, [ ] or [[ ]]. Here is an example showing when the arithmetic operator is useful:

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


It's just a convention that a 0 exit code means success. EXIT_SUCCESS will be 0 on almost every modern system.

EDIT:

"why both test 0 and test 1 returns 0(success) ?"

That's a completely different question. The answer is that passing a single argument to test always results in success unless that argument is the null string (""). See the Open Group documentation.


Typically programs return zero for success, non-zero for failure; false returns 1 because it's a convenient non-zero value, but generally any non-zero value means failure of some sort, and many programs will return different non-zero values to indicate different failure modes


it's a convention dated back to the early days of Unix.

By convention, all system calls return 0 if succeed, non-zero otherwise, because then different numbers can be used to indicate different reason of failure.

Shells follow this convention, 0 means last command succeeded, non-zero otherwise. Similarly, the non-zero return value comes in handy for output error messages: e.g. 1: "brain dead", 2: "heartless", and so on.


AFAIK this come from the C convention that you should return 0 if succeded. See:

man close

Most of the C (POSIX) api is build like this. http://en.wikipedia.org/wiki/C_POSIX_library


Your trying to equate true/false with success/failure.

They are two completely, although subtly at first, different dichotomies!

In shell scripting, there is no such thing as true/false. Shell 'expressions' aren't interpreted as true/false. Rather, shell 'expressions' are processes that either succeed or fail.

Obviously, a process might fail for many reasons. Thus we need a larger set codes to map possible failures to. The positive integers do the trick. On the other hand, if the process succeeds, that means it did exactly what it was supposed to do. Since there is only one way to do that, we only need one code. 0 does the trick.

In C, we are creating a program. In a shell script, we are running a bunch of programs to get something done.

Difference!


Maybe a good way to remember it is :

  • Return codes answer "what's the answer?" true (or other result) or false
  • Exit codes answer "what's the problem?" exit code or no problem (0)


Although the question is not tagged for C programming and most answers seem to explain the real reason indirectly, I thought I'll answer, what I hope, a C programmer would find useful.

IMHO, the true source of ambiguity is the C standard. For example, in C11 draft spec's (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) Section "7.22.4.4 The exit function" vs Section "7.18 Boolean type and values <stdbool.h>", EXIT_SUCCESS is zero, which is what programs run from the linux shell cmdline are expected to return on success, whereas, bool true is non-zero (#define'ed to 1, but safer to call it non-zero. Why is that? Refer to https://stackoverflow.com/users/827263/keith-thompson 's comment in https://stackoverflow.com/a/40009047/4726668 ).

Quoting from C11 draft spec (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf):

7.22.4.4 The exit function

"

5 Finally, control is returned to the host environment. If the value of status is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned. If the value of status is EXIT_FAILURE, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.

"

vs

7.18 Boolean type and values <stdbool.h>

"

3 The remaining three macros are suitable for use in #if preprocessing directives. They are

true

which expands to the integer constant 1,

false

which expands to the integer constant 0, and

_ _bool_true_false_are_defined

which expands to the integer constant 1.

"

0

精彩评论

暂无评论...
验证码 换一张
取 消