Recent from talks
Nothing was collected or created yet.
Exit status
View on Wikipedia
In computing, the exit status (also exit code or exit value) of a terminated process is an integer number that is made available to its parent process (or caller). In DOS, this may be referred to as an errorlevel.
When computer programs are executed, the operating system creates an abstract entity called a process in which the book-keeping for that program is maintained. In multitasking operating systems such as Unix or Linux, new processes can be created by active processes. The process that spawns another is called a parent process, while those created are child processes. Child processes run concurrently with the parent process. The technique of spawning child processes is used to delegate some work to a child process when there is no reason to stop the execution of the parent. When the child finishes executing, it exits by calling the exit system call. This system call facilitates passing the exit status code back to the parent, which can retrieve this value using the wait system call.
Semantics
[edit]The parent and the child can have an understanding about the meaning of the exit statuses. For example, it is common programming practice for a child process to return (exit with) zero to the parent signifying success. Apart from this return value from the child, other information like how the process exited, either normally or by a signal may also be available to the parent process.
The specific set of codes returned is unique to the program that sets it. Typically it indicates success or failure. The value of the code returned by the function or program may indicate a specific cause of failure. On many systems, the higher the value, the more severe the cause of the error.[1] Alternatively, each bit may indicate a different condition, with these being evaluated by the or operator together to give the final value; for example, fsck does this.
Sometimes, if the codes are designed with this purpose in mind, they can be used directly as a branch index upon return to the initiating program to avoid additional tests.
AmigaOS
[edit]In AmigaOS, MorphOS and AROS, four levels are defined:
- OK 0
- WARN 5
- ERROR 10
- FAILURE 20
Shell and scripts
[edit]Shell scripts typically execute commands and capture their exit statuses.
For the shell's purposes, a command which exits with a zero exit status has succeeded. A nonzero exit status indicates failure. This seemingly counter-intuitive scheme is used so there is one well-defined way to indicate success and a variety of ways to indicate various failure modes. When a command is terminated by a signal whose number is N, a shell sets the variable $? to a value greater than 128. Most shells use 128+N, while ksh93 uses 256+N.
If a command is not found, the shell should return a status of 127. If a command is found but is not executable, the return status should be 126.[2] Note that this is not the case for all shells.
If a command fails because of an error during expansion or redirection, the exit status is greater than zero.
C language
[edit]The C programming language allows programs exiting or returning from the main function to signal success or failure by returning an integer, or returning the macros EXIT_SUCCESS and EXIT_FAILURE. On Unix-like systems these are equal to 0 and 1 respectively.[3] A C program may also use the exit() function specifying the integer status or exit macro as the first parameter.
The return value from main is passed to the exit function, which for values zero, EXIT_SUCCESS or EXIT_FAILURE may translate it to "an implementation defined form" of successful termination or unsuccessful termination.[citation needed]
Apart from zero and the macros EXIT_SUCCESS and EXIT_FAILURE, the C standard does not define the meaning of return codes. Rules for the use of return codes vary on different platforms (see the platform-specific sections).
DOS
[edit]In DOS terminology, an errorlevel is an integer exit code returned by an executable program or subroutine. Errorlevels typically range from 0 to 255.[4][5][6][7] In DOS there are only 256 error codes available, but DR DOS 6.0 and higher support 16-bit error codes at least in CONFIG.SYS.[6] With 4DOS and DR-DOS COMMAND.COM, exit codes (in batchjobs) can be set by EXIT n[6] and (in CONFIG.SYS) through ERROR=n.[6]
Exit statuses are often captured by batch programs through IF ERRORLEVEL commands.[4][6] Multiuser DOS supports a reserved environment variable %ERRORLVL% which gets automatically updated on return from applications. COMMAND.COM under DR-DOS 7.02 and higher supports a similar pseudo-environment variable %ERRORLVL% as well as %ERRORLEVEL%. In CONFIG.SYS, DR DOS 6.0 and higher supports ONERROR to test the load status and return code of device drivers and the exit code of programs.[6]
Java
[edit]In Java, any method can call System.exit(int status), unless a security manager does not permit it. This will terminate the currently running Java Virtual Machine. "The argument serves as a status code; by convention, a nonzero status code indicates abnormal termination."[8]
OpenVMS
[edit]In OpenVMS, success is indicated by odd values and failure by even values. The value is a 32-bit integer with sub-fields: control bits, facility number, message number and severity. Severity values are divided between success (Success, Informational) and failure (Warning, Error, Fatal).[9]
Plan 9
[edit]In Plan 9's C, exit status is indicated by a string passed to the exits() function, and function main() is type void.
POSIX
[edit]In Unix and other POSIX-compatible systems, the parent process can retrieve the exit status of a child process using the wait() family of system calls defined in POSIX header <wait.h>.[10] Of these, the waitid()[11] call retrieves the full exit status, but the older wait() and waitpid()[12] calls retrieve only the least significant 8 bits of the exit status.
The wait() and waitpid() interfaces set a status value of type int packed as a bitfield with various types of child termination information. If the child terminated by exiting (as determined by the WIFEXITED() macro; the usual alternative being that it died from an uncaught signal), SUS specifies that the low-order 8 bits of the exit status can be retrieved from the status value using the WEXITSTATUS() macro.
In the waitid() system call (added with SUSv1), the child exit status and other information are no longer in a bitfield but in the structure of type siginfo_t.[13]
POSIX-compatible systems typically use a convention of zero for success and nonzero for error.[14] Some conventions have developed as to the relative meanings of various error codes; for example GNU recommend that codes with the high bit set be reserved for serious errors.[3]
BSD-derived OS's have defined an extensive set of preferred interpretations: Meanings for 15 status codes 64 through 78 are defined in POSIX header <sysexits.h>.[15] These historically derive from sendmail and other message transfer agents, but they have since found use in many other programs.[16] It has been deprecated and its use is discouraged.[15]
The Advanced Bash-Scripting Guide has some information on the meaning of non-0 exit status codes.[17]
Windows
[edit]Microsoft Windows uses 32-bit unsigned integers as exit codes,[18][19] although the command interpreter treats them as signed.[20]
Exit codes are directly referenced, for example, by the command line interpreter CMD.exe in the errorlevel terminology inherited from DOS. The .NET Framework processes and the Windows PowerShell refer to it as the ExitCode property of the Process object.
See also
[edit]References
[edit]- ^ "Errorlevels". Rob van der Woude's Scripting Pages. Retrieved 2007-08-26.
- ^ "Shell command language - Exit Status for commands". The Open Group. Retrieved 2015-07-07.
- ^ a b "The GNU C Library Reference Manual 25.6.2: Exit Status". Gnu.org. Retrieved 2012-07-09.
- ^ a b Paul, Matthias R. (1997-05-01) [1993-10-01]. BATTIPs — Tips & Tricks zur Programmierung von Batchjobs (in German). 7: ERRORLEVEL abfragen. Archived from the original on 2017-08-23. Retrieved 2017-08-23.
{{cite book}}:|work=ignored (help) [1] [2] Archived 2017-09-11 at archive.today (NB. BATTIPS.TXT is part of MPDOSTIP.ZIP. The provided link points to a HTML-converted older version of the BATTIPS.TXT file.) [3] - ^ Auer, Eric; Paul, Matthias R.; Hall, Jim (2015-12-24) [2003-12-31]. "MS-DOS errorlevels". Archived from the original on 2015-12-24.
- ^ a b c d e f Paul, Matthias R. (1997-07-30) [1994-05-01]. NWDOS-TIPs — Tips & Tricks rund um Novell DOS 7, mit Blick auf undokumentierte Details, Bugs und Workarounds. Release 157 (in German) (3 ed.). Archived from the original on 2016-11-04. Retrieved 2014-08-06.
{{cite book}}:|work=ignored (help) (NB. NWDOSTIP.TXT is a comprehensive work on Novell DOS 7 and OpenDOS 7.01, including the description of many undocumented features and internals. The provided link points to a HTML-converted version of the file, which is part of theMPDOSTIP.ZIPcollection.) [4] - ^ Allen, William; Allen, Linda. "Windows 95/98/ME ERRORLEVELs". Archived from the original on 2011-07-07.
- ^ "Java 1.6.0 API". Sun Microsystems. Retrieved 2008-05-06.
- ^ "OpenVMS Format of Return Status Values". H71000.www7.hp.com. Archived from the original on 2012-03-19. Retrieved 2012-07-09.
- ^ – Base Definitions Reference, The Single UNIX Specification, Version 5 from The Open Group
- ^ – System Interfaces Reference, The Single UNIX Specification, Version 5 from The Open Group
- ^ – System Interfaces Reference, The Single UNIX Specification, Version 5 from The Open Group
- ^ "2.4.3 Signal Actions". The Open Group. Retrieved 2019-02-08.
- ^ "Chapter 6. Exit and Exit Status". Faqs.org. Retrieved 2012-07-09.
- ^ a b : preferable exit codes for programs – FreeBSD Library Functions Manual
- ^ Google search for «"sysexits.h" site:github.com» reports «About 3,540 results»; retrieved 2013-02-21.
- ^ "Exit Codes with Special Meanings".
- ^ "ExitProcess function". Retrieved 2016-12-16.
- ^ "GetExitCodeProcess function". Retrieved 2022-04-22.
- ^ "ExitCodes bigger than 255, possible?". Retrieved 2009-09-28.
Exit status
View on Grokipediaexit() function or by returning from the main() function in a program, with 0 conventionally denoting successful completion and any non-zero value signaling an error, failure, or other specific condition.[1]
The exit status is retrieved by the parent process using system calls such as wait() and waitpid(), which provide the low-order 8 bits of the status, or waitid(), which provides the full status value, allowing macros like WIFEXITED() and WEXITSTATUS() to interpret whether the child exited normally and extract the exact code.[2][3] In shell scripting and command-line environments, such as Bash, the exit status of the last executed command is stored in the special variable $? and influences control flow through constructs like if statements or the && and || operators, enabling error handling and automation. POSIX defines standard symbolic constants—EXIT_SUCCESS (0) for success and EXIT_FAILURE (1) for general failure—while higher values (e.g., 2 for misuse of shell builtins or 126/127 for command not executable or not found) follow common Unix conventions, though exact meanings beyond 0 and 1 are implementation-specific and not universally standardized.[1] This mechanism ensures reliable inter-process communication about execution results, forming a foundational element of process management in modern operating systems.
Fundamentals
Definition and Purpose
An exit status is an integer value returned by a process upon termination, providing a standardized way to indicate the result of its execution to the parent process or caller. In POSIX-compliant systems, this value is conveyed using the low-order 8 bits of an integer, effectively limiting the portable range to 0 to 255.[1] The core purpose of an exit status is to convey success—conventionally represented by 0—or various failure or error conditions through non-zero values, allowing parent processes, shells, or orchestrators to make informed decisions such as branching conditional logic, recording errors, or propagating termination signals. This facilitates robust error handling in scripted or automated workflows by enabling the inspection of outcomes without direct inter-process communication.[4][5] The mechanism traces its origins to early batch processing operating systems in the 1960s, exemplified by IBM's OS/360, where programs would set a return code in a designated register (such as register 15) to signal job completion or errors to the supervisor, supporting sequential execution and basic fault detection.[6] It evolved through the 1970s in multi-tasking environments to handle inter-process coordination, becoming integral to process hierarchies. A key principle is that the exit status remains associated with the specific terminating process and is retrieved by the parent via system calls like wait(), which blocks until the child terminates and populates a status structure; the parent then uses macros such as WEXITSTATUS to extract the low-order 8 bits as the actual exit value.[4] In POSIX systems, this supports the convention where 0 signifies successful completion and non-zero values denote errors.[7]Common Conventions
In many computing systems, particularly those influenced by Unix-like conventions, the exit status of a program or command is standardized as an 8-bit unsigned integer, ranging from 0 to 255, with values wrapping around modulo 256 due to this bit limitation.[8] This range ensures portability across implementations, as specified in the POSIX standard, which defines the exit status as the value returned by thewait() function's WEXITSTATUS macro.[8] The ISO C standard similarly supports this by allowing exit() to accept an integer argument, typically interpreted within 0 to 255 for compatibility, though it does not explicitly mandate the range.[9]
A value of 0 universally denotes successful completion or no error, while any non-zero value indicates failure or an error condition.[8] Common error codes within this framework include 1 for general or catch-all failures, such as syntax errors or unexpected conditions in programs. In shell environments, exit code 2 signifies misuse of shell builtins, like invalid arguments to commands such as shift or break. Higher values like 126 indicate that a command was found but is not executable (e.g., due to permission issues), and 127 denotes that the command was not found at all.[8]
Best practices recommend that programs assign low numbers (typically 1 through 125) to specific, application-defined errors to promote clarity and interoperability, reserving higher values (126 through 255) for system- or shell-specific issues, such as signal terminations (often represented as 128 plus the signal number).[10] This approach avoids conflicts with shell-reserved codes above 125, which may be used for special purposes like indicating fatal errors or invalid exit arguments.
These conventions have evolved primarily through the influence of the ISO C standard (ISO/IEC 9899), which established macros like EXIT_SUCCESS (0) and EXIT_FAILURE (non-zero), and POSIX (IEEE Std 1003.1), which formalized the 0-255 range and special codes for shells and utilities, promoting widespread adoption across operating systems and programming environments.[9][8]
Operating System Implementations
POSIX and Unix-like Systems
In POSIX-compliant systems and Unix-like operating systems, the exit status of a process is defined as the low-order 8 bits of the status argument provided upon termination, forming an 8-bit unsigned integer value ranging from 0 to 255.[1] This value is made available to the parent process through system calls that retrieve child process status.[11] The standard convention interprets an exit status of 0 as indicating successful completion, while any non-zero value signifies failure or an error condition.[8] In shell environments, the special parameter$? provides access to the exit status of the most recently executed foreground pipeline or command.[12]
This handling is formalized in the IEEE Std 1003.1 (POSIX.1) standard, which specifies the behavior in sections covering shell utilities like sh and system interfaces such as wait() and waitpid(). To retrieve the exit status from a child process, a parent uses waitpid() (or wait()), which stores the full status in an integer; if the child terminated normally, the WEXITSTATUS macro extracts the 8-bit exit value from this status word.[11]
When a process terminates due to an uncaught signal, the status word returned by waitpid() encodes the signal information rather than an exit value: the signal number is placed in the low-order 7 bits, with bit 7 set to indicate signal termination (resulting in a value of 128 + signal number in the low byte for querying via $? in shells).[11] If a core dump occurred, an additional flag (bit 7 of the low byte already set, with the core indication via WCOREDUMP) is included in the full status, though shell access via $? typically reflects only 128 + signal number without distinguishing the core dump.[13]
Implementations remain consistent across major Unix-like variants, including Linux, BSD derivatives (such as FreeBSD), and macOS, all of which adhere to POSIX requirements for status encoding and retrieval. GNU-specific extensions in systems like Linux further align with the 128 + signal convention for non-core-dump signal terminations in shell contexts.
Windows
In the Windows operating system, particularly under the NT kernel, the exit status of a process is represented as a 32-bit unsigned integer (DWORD). This value is explicitly set when a process terminates using theExitProcess function from the Windows API, which ends the calling process and all its threads, passing the specified exit code to the operating system. Developers can also retrieve this exit code post-termination via the GetExitCodeProcess function, which returns STILL_ACTIVE (259) if the process is running or the final DWORD otherwise.[14]
By convention in Windows applications and system components, an exit code of 0 denotes successful completion, while any non-zero value indicates failure or an error condition. Unlike some legacy systems limited to 8 bits, the 32-bit DWORD allows for a vast range of distinct codes (0 to 4,294,967,295), enabling finer-grained error reporting without truncation. This flexibility supports the use of standardized error formats such as NTSTATUS (kernel-mode status codes) or HRESULT (user-mode structured exceptions), which encode detailed failure information within the 32 bits; for example, the HRESULT value 0x80070002 corresponds to the Win32 error "file not found" (ERROR_FILE_NOT_FOUND). Applications often reserve code 1 for generic errors when more specific diagnostics are unavailable.[14][15]
In interactive environments like the Command Prompt (cmd.exe), the exit code from the previous command or program is stored in the %ERRORLEVEL% environment variable, which scripts and users can reference for error checking—much like the LASTEXITCODE automatic variable, which captures the exit code from the last external executable or native command invoked. Batch files commonly leverage %ERRORLEVEL% for conditional execution; the IF ERRORLEVEL n syntax evaluates to true if the current error level is greater than or equal to n, allowing branching logic such as IF ERRORLEVEL 1 [ECHO](/page/Echo) An error occurred to handle failures gracefully without halting the script.[16][17]
At the kernel level, threads within a process maintain individual exit codes set via ExitThread, retrievable using GetExitCodeThread even after termination. These thread-specific codes inform the process's overall state but do not directly override the process exit code; instead, the process termination status is finalized by the ExitProcess call from the primary thread or the return value from the process's entry point (e.g., WinMain or main), aggregating thread outcomes into a single DWORD reported to the parent process or system. This design ensures that multiprocess and multithreaded applications can propagate meaningful status information upward in the execution hierarchy.[18][19]
DOS
In MS-DOS and compatible systems, programs terminate by invoking interrupt 21h with function 4Ch in the AH register, passing an 8-bit return code in the AL register to indicate the outcome of execution. This mechanism allows the operating system to return control to the parent process, typically the COMMAND.COM shell, along with the specified code.[20] The return code serves as a simple status indicator, enabling basic error handling in scripts and applications. The exit code is an unsigned 8-bit integer ranging from 0 to 255, where 0 conventionally signifies successful completion and non-zero values represent errors or abnormal termination. In debugging tools such as DEBUG.COM, this value is often displayed in hexadecimal format as part of the AL register contents.[21] Common error codes include 2 for "file not found," which may occur when attempting to access a non-existent file via system calls.[22] Due to MS-DOS's single-tasking nature, exit codes are directly passed to the invoking COMMAND.COM instance without interference from concurrent processes, limiting their use to sequential execution flows.[23] In batch files, the exit status from the most recent command or program is stored in the ERRORLEVEL pseudo-variable and can be tested using conditional IF statements, such asIF ERRORLEVEL 1 GOTO error_handler, which evaluates true if the code is 1 or higher.[24] This syntax provides backward compatibility for scripting and remains functional in MS-DOS environments. The 8-bit DOS convention influenced early Windows implementations, including Windows 95 and 98 (collectively known as Win9x), which preserved ERRORLEVEL handling for DOS-compatible batch files.[25] However, it was largely superseded in the Windows NT kernel lineage by 32-bit exit codes retrievable via functions like GetExitCodeProcess, which return a full DWORD value for broader application needs.[26]
AmigaOS
In AmigaOS, the exit status system employs a multi-level convention that provides graded feedback beyond simple success or failure, using predefined numeric values to indicate severity. The standard levels are 0 for success (OK), 5 for warnings (non-critical issues that allow continuation), 10 for errors (moderate problems that typically abort scripts unless overridden), and 20 for failures (critical issues that always terminate execution).[27] These levels are based on AmigaDOS error codes, enabling programs and scripts to signal nuanced outcomes, such as recoverable cautions versus irrecoverable halts, which supports more robust error handling in multitasking environments.[28] Programs set these exit statuses using theExit() function from the dos.library, passing the desired code as an argument before termination, or in assembly by placing the value in register D0 upon exit.[29] In scripting contexts, the QUIT command specifies a return code to propagate the status, while the RC environment variable captures the outcome of executed commands for conditional logic, such as with IF WARN to check for level 5.[27] The FAILAT command further refines behavior by setting a threshold (e.g., FAILAT 15) above which scripts abort, allowing tolerance for warnings and minor errors.[28]
To query a child process's exit status, parents use synchronous calls like RunCommand() or System() with the WAIT flag, which return the child's code directly (0 for OK, 20 for FAIL, or -1 on launch failure).[30] For asynchronous processes, notification occurs via the DeathMessage structure in NP_NotifyOnDeathMessage, providing the primary return code in dm_ReturnCode and a secondary error from IoErr() in dm_Result2; alternatively, the process header's pr_ReturnCode field holds the value post-termination.[30]
This system originated with AmigaDOS in AmigaOS 1.0, released in 1985, and has remained consistent through versions up to 4.x, influencing scripting in the Workbench environment where CLI commands underpin tool automation and batch operations. Examples include returning 5 for non-serious warnings, such as user confirmation prompts in scripts via the ASK command; 10 for moderate errors like invalid arguments; and 20 for critical failures, such as disk full conditions during file operations.[31][27] These discrete levels pose portability challenges in cross-platform development, as they deviate from binary (0/non-zero) or broad-range (0-255) conventions in other systems, requiring conditional mapping in shared codebases.[28]
OpenVMS
In OpenVMS, the exit status is represented by a 32-bit condition value that encodes detailed information about the outcome of program execution or system service calls. This value consists of a facility code in bits <27:16> identifying the originating component, such as the operating system or a specific run-time library; a message code in bits <15:3> specifying the particular condition within that facility; and severity bits in <2:0> indicating the overall impact of the condition. The structure allows for precise error reporting and handling, integrating seamlessly with the OpenVMS condition handling facility.[32] Programs terminate and return an exit status using run-time library routines like LIBEXIT, which pass the 32-bit condition value to the parent process, calling image, or DCL command interpreter. LIBEXIT similarly terminates the current image, returning the status in register R0 without further condition value arguments, enabling the status to propagate to higher-level environments like DCL procedures.[33][34] Severity levels are encoded in the low-order bits of the condition value, providing a standardized way to classify outcomes: success (1, indicating normal completion), warning (4, for non-fatal issues where the operation partially succeeded), error (8, for recoverable failures), and fatal (12, for severe, unrecoverable errors that may compromise system stability). These levels facilitate automated decision-making in scripts and handlers. For example, the generic success condition SS$_NORMAL has a value of 1, with the success bit (<0>) set.[32] To query exit statuses of processes, developers use the SYS_EXIT_STATUS, which retrieves the final condition value for a terminated process, or related items for ongoing monitoring across local or cluster-wide processes. In DCL scripting, the IF statements—such as$IF $STATUS THEN for success (odd values) or $IF .NOT. $STATUS for failure (even values)—with $SEVERITY providing the extracted level for conditional branching.[35][36]
This condition value model has been a core feature since VMS version 1.0, released in October 1978, and integrates deeply with Record Management Services (RMS) for file I/O statuses and the RTL for broader application support, ensuring consistent error propagation across the system.[37][38]
Plan 9
Plan 9, a distributed operating system developed at Bell Laboratories beginning in 1992, diverges from Unix conventions by representing exit status as an arbitrary text string rather than an integer value. This design choice aligns with the system's philosophy of treating all resources uniformly as files accessible via the 9P protocol, emphasizing simplicity and extensibility. Processes terminate using theexits system call, which accepts a character string message (up to ERRLEN bytes) describing the exit condition; a null or empty string denotes successful completion, while a non-empty string typically indicates an error or diagnostic information.[39][40]
The parent process queries a child's exit status through the wait system call, which returns a Waitmsg structure containing the process ID, execution times, and the exit message—formatted as the process name, ID, and message if present—or via await, which populates a buffer with the full status text for parsing (e.g., using tokenize after appending a null terminator). Unhandled notes, Plan 9's equivalent to Unix signals, cause the process to exit with a status string prefixed by "suicide:" followed by the note name, such as "suicide: hangup" for a termination request. Unlike Unix systems, Plan 9 avoids core dumps, relying instead on the /proc file system for process inspection and status retrieval during debugging.[41][42][43]
This string-based mechanism integrates seamlessly with Plan 9's distributed model, where namespaces span multiple machines via 9P-mounted file systems; exit statuses from remote processes remain accessible by mounting their /proc directories, enabling status propagation across the network without specialized inter-process communication primitives. Process creation with rfork inherits the parent's namespace and status handling, allowing lightweight forking while maintaining consistent access to exit information.[40]
Programming Language Support
C Language
In the C programming language, theexit function, declared in the <stdlib.h> header, terminates the calling process and returns a status code to the host environment, allowing the program to communicate success or failure to the parent process or shell. According to the ISO C99 and C11 standards, the function signature is void exit(int status);, where the status argument is an integer whose value is passed unchanged to the environment, with conventional values of 0 or EXIT_SUCCESS indicating successful execution and any other value, such as EXIT_FAILURE, signaling an error. This mechanism provides a standardized way for C programs to end gracefully while preserving portability across compliant implementations.
The _exit function, typically declared in <unistd.h> as part of POSIX extensions, offers a lower-level alternative to exit by immediately terminating the process without invoking cleanup operations, such as calling functions registered with atexit or flushing standard I/O buffers. In contrast, exit performs these cleanup steps before termination, ensuring that resources like open files are properly closed and any registered handlers are executed, which is essential for maintaining program integrity in most scenarios.[1] Developers use _exit primarily in low-level contexts, such as after a fork to avoid unintended side effects in the child process.
For portability, the ISO C99 and C11 standards define the status parameter as a full int, but many operating systems, including Unix-like systems, truncate it to the low-order 8 bits when propagating the value to the parent, limiting the effective range to 0–255. This truncation occurs at the OS level, so programs relying on values beyond 8 bits may behave inconsistently across platforms.
When handling child processes created via fork, the C standard integrates with POSIX facilities through the <sys/wait.h> header, where the WEXITSTATUS macro extracts the 8-bit exit status from the overall status value returned by functions like wait. For instance, after wait(&status), applying WEXITSTATUS(status) yields the child's exit code if it terminated normally, enabling parent processes to inspect and respond to the child's outcome.
A common example is the return statement in main, which implicitly behaves as if exit were called with the returned value; thus, return 0; in main signals successful completion equivalent to exit(0). This equivalence is mandated by the ISO C standards to simplify program termination.
One limitation of C's exit status mechanism is the absence of direct encoding for signals or abnormal terminations within the status value itself, as it relies on the underlying OS to distinguish between normal exits and signal-induced deaths via separate status bits in wait results.
Java
In Java, the primary mechanism for terminating the Java Virtual Machine (JVM) and specifying an exit status is theSystem.exit(int status) method, which immediately halts the currently running JVM and passes the provided integer status to the underlying operating system.[44] This method invokes Runtime.getRuntime().exit(status) internally and does not return normally, ensuring abrupt termination after executing any registered shutdown hooks.[45]
The status argument is an int value, where 0 conventionally denotes successful execution and any non-zero value indicates failure or abnormal termination.[44] Although Java uses a full 32-bit integer, the operating system receiving the status may truncate it—for instance, POSIX-compliant systems like Unix and Linux limit exit codes to 8 bits (0–255), preserving only the least significant byte and interpreting negative values as 255 for -1.[1][46]
Uncaught exceptions in the main thread trigger the JVM's default uncaught exception handler, which prints the exception's stack trace to the standard error stream and terminates the JVM with a non-zero exit status, typically 1.[47][48] This behavior can be customized by implementing Thread.UncaughtExceptionHandler and setting it via Thread.setDefaultUncaughtExceptionHandler, allowing developers to log details or call System.exit with a tailored status before termination.
If the public static void main(String[] args) method completes normally without throwing an exception, the JVM implicitly shuts down with an exit status of 0, as the last non-daemon thread has terminated successfully.
The JVM provides portability by abstracting platform-specific exit mechanisms, mapping the Java status to the host OS's native conventions—such as the 8-bit limitation in POSIX environments—while maintaining consistent semantics across Windows, Unix-like systems, and others.[44][1]
To intercept or modify behavior before exit, shutdown hooks can be registered using Runtime.addShutdownHook(Thread), which execute during the JVM's orderly shutdown sequence triggered by System.exit or normal completion. Java agents, via instrumentation, can further control exits.
Python
In Python, the primary mechanism for terminating a program with an exit status is thesys.exit([arg]) function from the sys module, which raises a SystemExit exception to allow for cleanup operations such as executing finally clauses in try-except blocks.[49] The optional arg parameter defaults to None, which corresponds to an exit status of 0 indicating successful termination; if provided as an integer, it sets the status directly (with 0 for success and nonzero values, typically 1, for errors); non-integer arguments like strings are printed to standard error, and the program exits with status 1.[49] This exception-based approach ensures that the exit status can be propagated while permitting exception handling and debugging intervention.[50]
The SystemExit exception, a subclass of BaseException rather than Exception, is designed specifically for programmatic termination and is not caught by generic exception handlers unless explicitly targeted.[50] If SystemExit is caught, the exception's argument can be inspected or modified to adjust the exit status before re-raising it or continuing execution. Unhandled SystemExit instances result in the interpreter exiting with the specified status without printing a traceback. In contrast, unhandled exceptions other than SystemExit (such as RuntimeError) cause the program to terminate with exit status 1, printing a full traceback to standard error for diagnostics.[50] The exit status, when an integer, is passed directly to the operating system, adhering to conventions where 0 denotes success and 1 is used for most general errors, though values up to 127 are common to align with POSIX standards.[49]
For scenarios requiring immediate termination without cleanup—such as in multi-threaded or forked processes—Python provides os._exit(n) in the os module, which bypasses exception handling, atexit callbacks, and buffer flushing to exit the process directly with the integer status n.[51] This function is particularly useful in scripting environments where partial execution might leave resources in an inconsistent state, analogous to low-level C functions like _exit. In the reference CPython implementation, sys.exit ultimately maps to the C standard library's exit() function after handling the exception, ensuring the status is forwarded to the OS.[50] Alternative implementations adapt accordingly: Jython raises SystemExit but may invoke Java's System.exit() in certain contexts to terminate the JVM process with the status, while IronPython similarly propagates the integer status to the .NET runtime's environment.[52][53]
Best practices recommend explicitly raising SystemExit(1) (or another appropriate status) for error conditions in scripts, as this leverages Python's exception system for clarity and allows integration with try-except blocks without relying on function calls that might be intercepted. This approach has been supported since Python 2.5, released in 2006, enhancing portability and maintainability in modern codebases.[50] For example, the following code demonstrates proper error exit:
import sys
try:
# Some operation that might fail
raise ValueError("Invalid input")
except ValueError:
raise SystemExit(1) # Explicit error exit
import sys
try:
# Some operation that might fail
raise ValueError("Invalid input")
except ValueError:
raise SystemExit(1) # Explicit error exit
Scripting and Automation
Shell Scripts
In shell scripting, particularly in POSIX-compliant shells like sh and extensions in Bash, the exit status provides a mechanism for commands to communicate success or failure to the script, enabling robust automation and error handling. The special parameter$? holds the exit status of the most recently executed command or pipeline, with a value of 0 indicating success and any non-zero value signifying failure.[8][54]
Scripts propagate exit status to their parent process by default using the status of the last executed command, unless overridden by the exit builtin, which terminates the shell with a specified status n (an unsigned decimal integer between 0 and 255). If n is omitted, the script exits with the status of the prior command. For instance, exit 1 explicitly signals failure to the calling environment.[55]
Control flow in scripts often relies on exit status through conditional constructs. The if statement evaluates the exit status of a command list directly: if it is zero, the then branch executes; otherwise, the else branch may follow. Scripts can also query $? explicitly, as in if [ $? -eq 0 ]; then echo "Success"; fi, to branch based on the previous command's outcome. This approach is fundamental for error checking in automation tasks.[8][56]
The trap builtin enhances exit status handling by intercepting signals, including the pseudo-signal EXIT, which triggers commands upon script termination regardless of the exit reason. Trap handlers can inspect or modify the exit status via $? before final propagation, such as logging errors or cleanup: trap 'echo "Exiting with status $?"' EXIT. This is particularly useful for ensuring resources are released even on abnormal termination.[57]
To propagate errors automatically, the set -e option (errexit) causes the shell to exit immediately upon any simple command or pipeline returning a non-zero status, unless the command is part of a conditional or trap. This mode, often enabled at the script's start with #!/bin/sh -e or set -e, simplifies error handling in long scripts by halting execution on failure.[8][58]
In Bash, the pipefail option addresses limitations in pipeline exit status propagation, where the default is the status of the last command only. When enabled via set -o pipefail, the pipeline's status becomes the rightmost non-zero exit status (or 0 if all succeed), ensuring failures in earlier stages are not masked. For example:
set -o pipefail
false | true
echo $? # Outputs 1, not 0
set -o pipefail
false | true
echo $? # Outputs 1, not 0
Batch and Command Scripts
In Windows batch files executed by cmd.exe, the exit status of the most recently run command or program is stored in the %ERRORLEVEL% environment variable, which holds a numeric value typically indicating success (0) or failure (non-zero).[16] This variable allows scripts to query the outcome of operations for conditional logic, evolving from early DOS mechanisms where programs terminated via interrupt 21h (function 4Ch) to return an 8-bit code to the calling process.[60] In modern Windows environments, this integrates with the Win32 API, preserving compatibility while supporting 32-bit integer values for broader error reporting. To terminate a batch script and set its exit status, the EXIT command is used:EXIT n ends the entire script and returns code n to the parent process, while EXIT /B n exits only the current subroutine (or the script if not in a subroutine) without closing the cmd.exe session, leaving n as the new %ERRORLEVEL%.[61] For example, a script might end with EXIT /B 1 to signal failure from a subroutine, propagating the status for further checks.[62]
Conditional processing relies on the IF statement to evaluate %ERRORLEVEL%, such as IF %ERRORLEVEL% EQU 0 (echo Success) to execute a block only if the previous command succeeded exactly with code 0, or IF %ERRORLEVEL% NEQ 0 (echo Failure) for any error.[16] This exact-value checking distinguishes batch scripting from more flexible systems, requiring precise comparisons rather than ranges in basic usage.[25]
Batch scripts have limitations in handling exit statuses, particularly with pipelines (|), where %ERRORLEVEL% reflects only the exit code of the final command in the chain, discarding intermediate results unless explicitly captured via temporary variables or delayed expansion.[25] Unlike some environments, batch files lack built-in signal trapping for interrupts, relying solely on polled exit codes post-execution.[24]
In PowerShell, which extends batch-like command scripting, the ? automatic variable offers a boolean view, evaluating to false otherwise, simplifying success checks in scripts like if ($?) { Write-Output "Success" }.[17] This dual approach enhances cross-platform scripting while maintaining Windows heritage.