1

I discovered a strange behaviour (reproducible with zsh and bash on my system):

$ # here everything is still normal
$ bash -c 'exit 1';echo $?
1
$ bash -c 'exit 255';echo $?
255
$ zsh -c 'exit 255';echo $?
255
$ # now it get's crazy
$ bash -c 'exit 256';echo $?
0
$ zsh -c 'exit 256';echo $?
0
$ # (leaving away zsh for now, it is always reproducible with both)
$ bash -c 'exit 257';echo $?
1
$ bash -c 'exit 267';echo $?
11

So after 256 it begins to count from 1 again. But why?

The bash man page does not indicate that there is a maximum number:

   exit [n]
          Cause  the  shell  to exit with a status of n.  If n is omitted,
          the exit status is that of the last command executed.  A trap on
          EXIT is executed before the shell terminates.

And this is highly confusing behaviour. If programs depend on that, it could lead to big problems.

So why does this happen? And why is not it documented?

x64, Fedora 26

  • 1
    It is documented in the EXIT STATUS section: "Exit statuses fall between 0 and 255, though, as explained below, the shell may use values above 125 specially." – xhienne Sep 26 '17 at 23:47
3

The POSIX manual page for the shell command exit(1p) states:

SYNOPSIS

exit [n]

DESCRIPTION

The exit utility shall cause the shell to exit with the exit status specified by the unsigned decimal integer n. If n is specified but its value is not between 0 and 255 inclusive, the exit status is undefined.

So for shells conforming to POSIX specifications, this behavior isn't undocumented, but it is also not necessarily portable.

2

It is documented in the manual of the exit system function:

The value of status may be 0, EXIT_SUCCESS, EXIT_FAILURE, [CX] [Option Start] or any other value, though only the least significant 8 bits (that is, status & 0377) shall be available to a waiting parent process. [Option End]

Linux seems to stick to the standard very closely, and it doesn't let more than just the last 8 bits through.

  • 1
    Note that waitid() lets you access more than 8 bits of the return code on some systems, not Linux yet. It seems unlikely shells will start using that straight away. See austingroupbugs.net/view.php?id=594 austingroupbugs.net/view.php?id=947 austingroupbugs.net/view.php?id=597 austingroupbugs.net/view.php?id=1026 (the issue seems dear to @Schily, his shell already uses it and makes it available as ${.sh.code} – Stéphane Chazelas Sep 26 '17 at 22:07
  • Sorry ${.sh.status}, not ${.sh.code} (which is the si_code) – Stéphane Chazelas Sep 26 '17 at 22:23
  • @StéphaneChazelas Interesting, but I couldn't verify it on Linux. I tried int main() { pid_t p; siginfo_t info; p = fork(); if(0==p) _exit(0xAABBCCDD); if(0>p){perror(0);exit(1);} waitid(P_ALL,0,&info,WEXITED); printf("%X\n", info.si_code); printf("%X\n", info.si_status); } and got 1 and DD which seems to indicate that indeed only the last 8 bits make it through. – PSkocik Sep 26 '17 at 22:26
  • Yes, like I said not on Linux yet. See Eric Blake commenting on whether the Kernel developers would agree to extend it in the first POSIX ticket I gave. You can test on FreeBSD or on SysV descendants like Solaris – Stéphane Chazelas Sep 26 '17 at 22:30
  • @StéphaneChazelas Sorry, I'm tired & I missed that part. It would sure be great if Linux let more of the exit bits though! – PSkocik Sep 26 '17 at 22:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.