Bash – Is the exit status of a command implemented by the command or a shell process which executes the command

bashcommandexit-status

From bash manual

The exit status of an executed command is the value returned by the
waitpid system call or equivalent function. Exit statuses fall between
0 and 255, though, as explained below, the shell may use values above
125 specially. Exit statuses from shell builtins and compound commands
are also limited to this range. Under certain circumstances, the shell
will use special values to indicate specific failure modes.

  1. What determines the exit status of an executed command,

    • the command itself,
      i.e. does the implementation of the command determines the exit status, i.e. is determination of the exit status of an executed command an inherent property of the command, just like parsing the command line arguments to an execution of a command is inherent to the command, or
    • the shell process which executed the command?
      The waitpid system call mentioned above seems to me that the exit
      status of an executed command is implemented by the shell process, and the command itself has nothing to do with the exit status of an execution of itself.
  2. In a bash process, when can we get the exit status of an executed
    command,

    • only when the bash process is interactive, or
    • regardless of whether the bash process is interactive or non-interactive? When a bash process is non-interactive, how can we
      retrieve the exit status of a command executed in the bash process?

Best Answer

The command itself can provide the exit status, via the argument to an exit() system call. The shell (or other program, how about system() in Perl or PHP?) can pick up a child process' exit status via wait() or waitpid() system calls. The Unix/Linux/*BSD kernels all arrange to deliver SIGCHLD to the parent process when a child process changes status.

But there are other circumstances. A process terminated via SIGKILL doesn't have a chance to exit. A process that dereferences an invalid address won't have a chance to call exit() unless it has a SIGSEGV signal handler installed. In those cases, the kernel calculates an exit status that includes a bit that means "killed by a signal", and what signal caused the process to get killed. That kernel-calculated exit status is delivered to the killed process' parent process.