Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> sys.exit(1) # Python exits with error code 1 on EPIPE

Should be 141 instead of 1. Convention is that when a program dies because of a signal, the exit code should be 128 + the signal number, in this case 13. So, 128 + 13 = 141.

Here are some examples:

  $ ( yes; >&2 echo $? ) | head -0 
  141
  $ ( ping localhost; >&2 echo $? ) | head -0 
  141
  $ ( perl -E 'say "y" while 1'; >&2 echo $? ) | head -0 
  141
In python, when using signal(SIGPIPE, SIG_DFL), you get the correct behaviour (at least regarding the exit status):

  $ ( python -c '                                                  
  import sys                                                                    
  from signal import signal, SIGPIPE, SIG_DFL     
  signal(SIGPIPE,SIG_DFL)                    
  while True: print("y");                         
  ' ; >&2 echo $? ) | head -0
  141
> Do not set SIGPIPE’s disposition to SIG_DFL in order to avoid BrokenPipeError. Doing that would cause your program to exit unexpectedly also whenever any socket connection is interrupted while your program is still writing to it.

Regarding that socket behavior, if that's the standard in other programming environments, why is it an issue from python's perspective? Doesn't seem worth eschewing standard unix conventions. I mean, it says "unexpectedly", but isn't it actually the expected behavior? Unexpected is for python to say that what's default (SIG_DFL) is unexpected.



> Convention is that when a program dies because of a signal, the exit code should be 128 + the signal number

Note that this is incorrect. This is a common misconception about POSIX/UNIX. Not every process exits with an exit code; that's right, a process can exit without an integer exit code! A process exits with either a) an exit code, _OR_ b) the signal which terminated the program (as a separate status in the underlying struct, rather than as an exit code).

Many shells–including bash–provide an abstraction that translates process exits caused by signals into 128+signo, but this is only to fake an exit code for processes as seen when executed within the shell; this is a nonstandard and shell-specific abstraction.

Look at the man page for waitpid() for details. There are macros to test for the difference between an exit status with a code vs. an exit due to a signal; eg. WIFEXITED() and WIFSIGNALED(). WEXITSTATUS() will return the exit status... if one exists because WIFEXITED() tests truthful. Hint: if you're used to seeing your shell report 128+signo, that's because your particular shell happens to check waitpid() for WIFSIGNALED() and sets $? to 128+signo for you… as an abstracted convenience.

tldr; An exit code, vs. an exit caused by a signal, are mutually exclusive statuses. Processes terminated directly by a signal do not have an integer exit code; your shell may provide a fabricated exit code to make things appear simpler, but that is shell-specific and NOT a standard convention provided by POSIX or UNIX.


You're right. waitpid(2) does document this.

I was also surprised to find that, at least on my linux system, the status integer returned by wait encodes the signal number in the least significant bits and the exit status in the most significant bits. So, when a process dies from SIGPIPE, the status (as set by wait(&status)) is 13; when it does exit(1), it's 256; when it does exit(2), it's 512; when it does exit(3), it's 768, etc. Maybe that encoding was done to avoid misuse from people seeing their exit codes in the raw status returned by wait, and thinking they didn't need to use the macros.

In any case, this does mean that, indeed, while the shell does display the same status for both `yes | head -0` and `(exit 141)`, they are in fact different.

TIL


That's why we love Unix shell: it takes everything we type and removes the types. :-)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: