🚪 The exit Command

Exit the Shell or Script with Status Code

exit [n]

Overview

The exit command is a shell built-in that terminates the current shell session or script and returns control to the parent process. It's one of the most fundamental commands in shell scripting, used to end scripts gracefully and communicate success or failure through exit status codes.

Primary Functions:

  • Exit the current shell or terminal session
  • Terminate a running script
  • Return an exit status code to the parent process
  • Signal success (0) or failure (non-zero) of operations
  • Control script flow and error handling
  • Enable conditional execution in pipelines

Exit Status Codes:

  • 0: Success - command completed successfully
  • 1-255: Various error conditions
  • 126: Command found but not executable
  • 127: Command not found
  • 128+n: Fatal signal n (e.g., 130 = Ctrl+C, signal 2)
  • 255: Exit status out of range
Built-in vs External:

Exit is a shell built-in command, not an external program. This means it's part of the shell itself (bash, sh, zsh, etc.) and can directly affect the current shell process. When you type exit in a terminal, that terminal session ends. When used in a script, the script terminates but the terminal remains open.

Detailed Examples

Example 1

Basic Exit from Interactive Shell

The simplest use of exit - terminating your current shell session.

# You're working in a terminal $ whoami craig $ pwd /home/craig # Exit the shell $ exit
[Terminal window closes or SSH session ends] logout Connection to server closed.
Explanation: When you type exit in an interactive shell, it terminates that shell session. In a terminal window, this typically closes the window. In an SSH session, it logs you out and closes the connection. The default exit status is the status of the last command executed before exit.
Shortcuts: You can also exit a shell by pressing Ctrl+D (End-of-File), which has the same effect as typing exit. The logout command also works in login shells.
Example 2

Exit with Explicit Status Code

Specifying an exit status to communicate success or failure.

#!/bin/bash # script1.sh - Exit with success echo "Processing data..." # Do some work... echo "Operation completed successfully!" # Exit with status 0 (success) exit 0
#!/bin/bash # script2.sh - Exit with failure echo "Checking configuration..." if [ ! -f "/etc/myapp.conf" ]; then echo "Error: Configuration file not found!" exit 1 # Exit with status 1 (failure) fi echo "Configuration OK" exit 0
# Run the scripts and check their exit status $ ./script1.sh Processing data... Operation completed successfully! $ echo $? 0 $ ./script2.sh Checking configuration... Error: Configuration file not found! $ echo $? 1
Explanation: The exit n command terminates the script and returns status code n to the parent process. By convention, 0 means success and non-zero means failure. The special variable $? holds the exit status of the last command executed.
Best Practice: Always use exit 0 for successful completion and non-zero values (typically 1-255) for errors. This allows other scripts and commands to determine if your script succeeded.
Example 3

Using Different Exit Codes for Different Errors

Implement meaningful exit codes to distinguish between different error conditions.

#!/bin/bash # backup-script.sh - Backup with meaningful exit codes SOURCE_DIR="/home/craig/documents" BACKUP_DIR="/backup" BACKUP_FILE="backup-$(date +%Y%m%d).tar.gz" # Exit code definitions EXIT_SUCCESS=0 EXIT_SOURCE_NOT_FOUND=1 EXIT_BACKUP_DIR_NOT_FOUND=2 EXIT_NO_SPACE=3 EXIT_COMPRESSION_FAILED=4 EXIT_PERMISSION_DENIED=5 # Check if source directory exists if [ ! -d "$SOURCE_DIR" ]; then echo "Error: Source directory not found: $SOURCE_DIR" exit $EXIT_SOURCE_NOT_FOUND fi # Check if backup directory exists if [ ! -d "$BACKUP_DIR" ]; then echo "Error: Backup directory not found: $BACKUP_DIR" exit $EXIT_BACKUP_DIR_NOT_FOUND fi # Check if we have write permission if [ ! -w "$BACKUP_DIR" ]; then echo "Error: No write permission for $BACKUP_DIR" exit $EXIT_PERMISSION_DENIED fi # Check available space (simplified) AVAILABLE=$(df -k "$BACKUP_DIR" | tail -1 | awk '{print $4}') if [ "$AVAILABLE" -lt 1048576 ]; then # Less than 1GB echo "Error: Insufficient disk space in $BACKUP_DIR" exit $EXIT_NO_SPACE fi # Perform backup echo "Creating backup: $BACKUP_FILE" tar -czf "$BACKUP_DIR/$BACKUP_FILE" "$SOURCE_DIR" 2>/dev/null if [ $? -ne 0 ]; then echo "Error: Backup compression failed" exit $EXIT_COMPRESSION_FAILED fi echo "Backup completed successfully: $BACKUP_DIR/$BACKUP_FILE" exit $EXIT_SUCCESS
# Calling script that checks exit codes #!/bin/bash # run-backup.sh ./backup-script.sh case $? in 0) echo "✓ Backup successful" ;; 1) echo "✗ Source directory not found" # Maybe try alternate source ;; 2) echo "✗ Backup directory not found" # Maybe create it ;; 3) echo "✗ Insufficient disk space" # Send alert to admin ;; 4) echo "✗ Compression failed" # Log error details ;; 5) echo "✗ Permission denied" # Run with sudo? ;; *) echo "✗ Unknown error" ;; esac
Explanation: Using different exit codes allows calling scripts or processes to distinguish between different types of failures and take appropriate action. This is crucial for robust automation and error handling.
Real-World Use: Production scripts use meaningful exit codes so monitoring systems can categorize failures, send appropriate alerts, and trigger specific recovery actions based on the type of error.
Example 4

Early Exit on Error

Exit immediately when an error is detected to prevent cascading failures.

#!/bin/bash # deployment-script.sh - Exit early on any error set -e # Exit immediately if any command fails echo "Starting deployment..." # This will exit if directory doesn't exist cd /opt/application || exit 1 echo "Stopping service..." systemctl stop myapp || exit 2 echo "Backing up current version..." cp -r current current.backup || exit 3 echo "Deploying new version..." tar -xzf /tmp/newversion.tar.gz || exit 4 echo "Starting service..." systemctl start myapp || exit 5 echo "Verifying service..." sleep 5 systemctl is-active myapp || exit 6 echo "Deployment completed successfully!" exit 0
Explanation: The || exit N pattern checks if the previous command failed (non-zero exit status) and immediately exits with a specific error code. The set -e option at the top makes the script exit automatically on any command failure, providing a safety net.
Important: Using set -e can have unexpected consequences in complex scripts. Commands in conditionals, pipelines, or with ||/&& may not trigger automatic exit. Always test thoroughly!
Better Approach: Instead of set -e, explicitly check important commands:
if ! systemctl stop myapp; then echo "Failed to stop service" exit 2 fi
Example 5

Exit from Functions

Using return vs exit in shell functions, and how they differ.

#!/bin/bash # function-exit-demo.sh # Function using return (correct for functions) check_file_exists() { local file="$1" if [ -f "$file" ]; then echo "File exists: $file" return 0 # Success - continues script else echo "File not found: $file" return 1 # Failure - continues script fi } # Function using exit (wrong - exits entire script!) check_file_exists_wrong() { local file="$1" if [ -f "$file" ]; then echo "File exists: $file" exit 0 # Exits the entire script! else echo "File not found: $file" exit 1 # Exits the entire script! fi } echo "Testing with return (correct):" check_file_exists "/etc/passwd" echo "Still running after function..." check_file_exists "/nonexistent" echo "Still running even after error..." echo "" echo "Testing with exit (wrong):" check_file_exists_wrong "/etc/passwd" echo "You won't see this line!" # Never executed
Testing with return (correct): File exists: /etc/passwd Still running after function... File not found: /nonexistent Still running even after error... Testing with exit (wrong): File exists: /etc/passwd [Script terminates - remaining lines never execute]
Explanation: In functions, use return to exit the function and return control to the calling code. Use exit only when you want to terminate the entire script. Using exit in a function will end the entire script, not just the function!
Return vs Exit:
  • return: Exits the function, returns status to caller, script continues
  • exit: Exits the entire script (or shell), returns status to parent process
  • Both accept status codes: return 0, exit 1
  • Return is only valid inside functions
  • Exit works anywhere in script or interactive shell
Example 6

Exit in Subshells and Pipelines

Understanding how exit behaves in subshells, which is crucial for complex scripts.

#!/bin/bash # subshell-exit-demo.sh echo "=== Exit in Subshell (Parentheses) ===" echo "Main script running..." ( echo "Inside subshell" exit 1 # Only exits the subshell! echo "This won't print" ) echo "Main script still running! Subshell exited." echo "Subshell exit status: $?" echo "" echo "=== Exit in Command Substitution ===" result=$( echo "Processing..." exit 5 # Only exits this command substitution echo "Never reached" ) echo "After command substitution" echo "Command substitution exit status: $?" echo "" echo "=== Exit in Pipeline ===" cat /etc/passwd | ( while read line; do if [[ "$line" == *"root"* ]]; then echo "Found root!" exit 0 # Only exits this part of pipeline fi done exit 1 ) echo "After pipeline" echo "Pipeline exit status: $?" echo "" echo "Main script ending with exit 0" exit 0
=== Exit in Subshell (Parentheses) === Main script running... Inside subshell Main script still running! Subshell exited. Subshell exit status: 1 === Exit in Command Substitution === After command substitution Command substitution exit status: 5 === Exit in Pipeline === Found root! After pipeline Pipeline exit status: 0 Main script ending with exit 0
Explanation: When you use exit in a subshell (created by parentheses, command substitution, or pipelines), it only exits that subshell, not the main script. The main script continues executing. This is important for understanding script flow and avoiding unexpected behavior.
Gotcha: If you want to exit the main script from within a subshell, you need to check the subshell's exit status and explicitly exit:
(command; exit 1) if [ $? -ne 0 ]; then exit 1 fi
Example 7

Trap and Exit - Cleanup on Exit

Using trap to ensure cleanup code runs regardless of how the script exits.

#!/bin/bash # cleanup-on-exit.sh - Proper cleanup using trap TEMP_DIR=$(mktemp -d) LOCKFILE="/var/run/myscript.lock" # Cleanup function cleanup() { local exit_code=$? echo "" echo "Cleaning up..." # Remove temporary directory if [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" echo " Removed temp directory: $TEMP_DIR" fi # Remove lock file if [ -f "$LOCKFILE" ]; then rm -f "$LOCKFILE" echo " Removed lock file: $LOCKFILE" fi echo "Cleanup complete. Exiting with code: $exit_code" exit $exit_code } # Set trap to call cleanup on EXIT signal trap cleanup EXIT # Also trap for specific signals trap 'echo "Caught SIGINT (Ctrl+C)"; exit 130' INT trap 'echo "Caught SIGTERM"; exit 143' TERM echo "Script starting..." echo "Creating temporary files in: $TEMP_DIR" # Create lock file touch "$LOCKFILE" echo "Created lock file: $LOCKFILE" # Simulate some work echo "Processing..." sleep 2 # Create some temp files touch "$TEMP_DIR/file1.tmp" touch "$TEMP_DIR/file2.tmp" echo "Created temporary files" # Simulate different exit scenarios read -p "Choose exit method (1=normal, 2=error, 3=Ctrl+C): " choice case $choice in 1) echo "Exiting normally..." exit 0 ;; 2) echo "Simulating error..." exit 1 ;; 3) echo "Waiting for Ctrl+C..." sleep 30 ;; esac
Script starting... Creating temporary files in: /tmp/tmp.Xz7K9mR2pL Created lock file: /var/run/myscript.lock Processing... Created temporary files Choose exit method (1=normal, 2=error, 3=Ctrl+C): 1 Exiting normally... Cleaning up... Removed temp directory: /tmp/tmp.Xz7K9mR2pL Removed lock file: /var/run/myscript.lock Cleanup complete. Exiting with code: 0
Explanation: The trap command sets up a function to run when the script exits, regardless of whether it's a normal exit, an error exit, or termination by signal (Ctrl+C, kill, etc.). This ensures cleanup code always runs, preventing resource leaks and leaving the system in a clean state.
Real-World Use: Production scripts use trap for cleanup of:
  • Temporary files and directories
  • Lock files
  • Database connections
  • Mounted filesystems
  • Background processes
  • Network connections
Example 8

Exit Status in Conditional Chains

How exit status affects && and || operators in command chains.

#!/bin/bash # conditional-chains.sh echo "=== AND Operator (&&) ===" echo "Only continues if previous command succeeds (exit 0)" # All succeed - all execute true && echo "Step 1" && echo "Step 2" && echo "Step 3" echo "" # Failure stops the chain true && echo "Step 1" && false && echo "Never reached" echo "" echo "=== OR Operator (||) ===" echo "Only continues if previous command fails (exit non-zero)" # First succeeds - stops true || echo "Not executed" || echo "Not executed either" echo "" # Keep trying until one succeeds false || false || echo "Finally succeeded!" || echo "Not needed" echo "" echo "=== Combined && and || ===" # Common pattern: command || exit mkdir /tmp/testdir || exit 1 echo "Directory created" # Check if file exists, else exit [ -f "/etc/passwd" ] || { echo "File not found!"; exit 1; } echo "File check passed" # Success notification ./important-script.sh && echo "SUCCESS" || { echo "FAILED"; exit 1; } echo "" echo "=== Practical Examples ===" # Deployment pattern cd /opt/application || exit 1 systemctl stop myapp || exit 2 git pull || exit 3 systemctl start myapp || exit 4 echo "Deployment successful" # Backup pattern tar -czf backup.tar.gz data/ || { echo "Backup failed"; exit 1; } scp backup.tar.gz remote:/backups/ || { echo "Upload failed"; exit 2; } rm backup.tar.gz || { echo "Cleanup warning"; } # Non-fatal echo "Backup completed"
Explanation: The && operator only executes the next command if the previous one succeeded (exit 0). The || operator only executes if the previous command failed (non-zero exit). These operators provide concise error handling and conditional execution based on exit status.
Operator Cheat Sheet:
  • cmd1 && cmd2 - Run cmd2 only if cmd1 succeeds
  • cmd1 || cmd2 - Run cmd2 only if cmd1 fails
  • cmd1 && cmd2 || cmd3 - Run cmd2 if cmd1 succeeds, else run cmd3
  • cmd || exit 1 - Exit if cmd fails (common pattern)
Example 9

Exit Without Specifying Code - Inheriting Status

When you don't specify an exit code, the script exits with the status of the last command.

#!/bin/bash # inherit-status.sh echo "=== Script 1: Exit without code after success ===" cat <<'EOF' > script1.sh #!/bin/bash ls /etc/passwd exit # No code specified - inherits 0 from successful ls EOF chmod +x script1.sh ./script1.sh echo "Script 1 exit status: $?" echo "" echo "=== Script 2: Exit without code after failure ===" cat <<'EOF' > script2.sh #!/bin/bash ls /nonexistent/file 2>/dev/null exit # No code specified - inherits non-zero from failed ls EOF chmod +x script2.sh ./script2.sh echo "Script 2 exit status: $?" echo "" echo "=== Script 3: No explicit exit ===" cat <<'EOF' > script3.sh #!/bin/bash echo "First command" echo "Second command" false # Last command fails # No exit statement - script ends with status of last command EOF chmod +x script3.sh ./script3.sh echo "Script 3 exit status: $?" echo "" echo "=== Script 4: Proper explicit exit ===" cat <<'EOF' > script4.sh #!/bin/bash echo "Processing..." false # This fails echo "More processing..." exit 0 # Explicitly exit with success despite earlier failure EOF chmod +x script4.sh ./script4.sh echo "Script 4 exit status: $?" # Cleanup rm -f script{1,2,3,4}.sh
=== Script 1: Exit without code after success === /etc/passwd Script 1 exit status: 0 === Script 2: Exit without code after failure === Script 2 exit status: 2 === Script 3: No explicit exit === First command Second command Script 3 exit status: 1 === Script 4: Proper explicit exit === Processing... More processing... Script 4 exit status: 0
Explanation: If you use exit without specifying a code, or if your script ends without an explicit exit statement, the exit status will be the status of the last command executed. This can lead to unexpected results if you're not careful.
Best Practice: Always explicitly specify your exit status with exit 0 for success or exit N for errors. Don't rely on inheriting the last command's status - it makes your script's behavior unclear and error-prone.
Example 10

Complete Script with Comprehensive Exit Handling

A production-quality script demonstrating all exit best practices.

#!/bin/bash # database-backup.sh - Production database backup script # Demonstrates comprehensive exit handling # Exit codes readonly EXIT_SUCCESS=0 readonly EXIT_INVALID_ARGS=1 readonly EXIT_DEPENDENCY_MISSING=2 readonly EXIT_DB_CONNECTION_FAILED=3 readonly EXIT_BACKUP_FAILED=4 readonly EXIT_UPLOAD_FAILED=5 readonly EXIT_CLEANUP_FAILED=6 # Configuration readonly SCRIPT_NAME=$(basename "$0") readonly BACKUP_DIR="/var/backups/database" readonly TEMP_DIR=$(mktemp -d) readonly LOG_FILE="/var/log/database-backup.log" # Logging function log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } # Error handling function error_exit() { local message="$1" local exit_code="${2:-1}" log "ERROR: $message" exit "$exit_code" } # Cleanup function cleanup() { local exit_code=$? log "Performing cleanup..." if [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" || log "WARNING: Failed to remove temp directory" fi # Log final status if [ $exit_code -eq 0 ]; then log "Backup completed successfully" else log "Backup failed with exit code: $exit_code" fi exit $exit_code } # Set trap for cleanup trap cleanup EXIT INT TERM # Validate arguments if [ $# -ne 1 ]; then echo "Usage: $SCRIPT_NAME <database_name>" echo "Example: $SCRIPT_NAME production_db" exit $EXIT_INVALID_ARGS fi DB_NAME="$1" # Start script log "=========================================" log "Starting backup for database: $DB_NAME" log "=========================================" # Check dependencies log "Checking dependencies..." for cmd in mysqldump gzip aws; do if ! command -v "$cmd" &>/dev/null; then error_exit "Required command not found: $cmd" $EXIT_DEPENDENCY_MISSING fi done log "All dependencies found" # Create backup directory if needed if [ ! -d "$BACKUP_DIR" ]; then log "Creating backup directory: $BACKUP_DIR" mkdir -p "$BACKUP_DIR" || error_exit "Failed to create backup directory" $EXIT_BACKUP_FAILED fi # Test database connection log "Testing database connection..." if ! mysql -e "SELECT 1" "$DB_NAME" &>/dev/null; then error_exit "Cannot connect to database: $DB_NAME" $EXIT_DB_CONNECTION_FAILED fi log "Database connection successful" # Perform backup TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$TEMP_DIR/${DB_NAME}_${TIMESTAMP}.sql" COMPRESSED_FILE="${BACKUP_FILE}.gz" log "Creating database dump..." if ! mysqldump "$DB_NAME" > "$BACKUP_FILE" 2>>"$LOG_FILE"; then error_exit "Database dump failed" $EXIT_BACKUP_FAILED fi # Verify backup file was created and has content if [ ! -s "$BACKUP_FILE" ]; then error_exit "Backup file is empty" $EXIT_BACKUP_FAILED fi FILE_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) log "Backup created: $BACKUP_FILE (Size: $FILE_SIZE)" # Compress backup log "Compressing backup..." if ! gzip "$BACKUP_FILE" 2>>"$LOG_FILE"; then error_exit "Compression failed" $EXIT_BACKUP_FAILED fi log "Backup compressed: $COMPRESSED_FILE" # Move to backup directory FINAL_FILE="$BACKUP_DIR/$(basename "$COMPRESSED_FILE")" if ! mv "$COMPRESSED_FILE" "$FINAL_FILE"; then error_exit "Failed to move backup to final location" $EXIT_BACKUP_FAILED fi log "Backup saved to: $FINAL_FILE" # Upload to S3 (if configured) if [ -n "${AWS_S3_BUCKET:-}" ]; then log "Uploading to S3: s3://$AWS_S3_BUCKET/backups/" if ! aws s3 cp "$FINAL_FILE" "s3://$AWS_S3_BUCKET/backups/" 2>>"$LOG_FILE"; then error_exit "S3 upload failed" $EXIT_UPLOAD_FAILED fi log "Upload completed successfully" fi # Clean old backups (keep last 7 days) log "Cleaning old backups (keeping 7 days)..." find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +7 -delete 2>>"$LOG_FILE" # Success! log "Backup process completed successfully!" exit $EXIT_SUCCESS
[2025-01-15 14:23:45] ========================================= [2025-01-15 14:23:45] Starting backup for database: production_db [2025-01-15 14:23:45] ========================================= [2025-01-15 14:23:45] Checking dependencies... [2025-01-15 14:23:45] All dependencies found [2025-01-15 14:23:45] Creating backup directory: /var/backups/database [2025-01-15 14:23:45] Testing database connection... [2025-01-15 14:23:46] Database connection successful [2025-01-15 14:23:46] Creating database dump... [2025-01-15 14:23:52] Backup created: /tmp/tmp.Xz7K9mR2pL/production_db_20250115_142346.sql (Size: 2.3G) [2025-01-15 14:23:52] Compressing backup... [2025-01-15 14:24:15] Backup compressed: /tmp/tmp.Xz7K9mR2pL/production_db_20250115_142346.sql.gz [2025-01-15 14:24:15] Backup saved to: /var/backups/database/production_db_20250115_142346.sql.gz [2025-01-15 14:24:15] Uploading to S3: s3://my-backups/backups/ [2025-01-15 14:24:23] Upload completed successfully [2025-01-15 14:24:23] Cleaning old backups (keeping 7 days)... [2025-01-15 14:24:23] Backup process completed successfully! [2025-01-15 14:24:23] Performing cleanup... [2025-01-15 14:24:23] Backup completed successfully
Explanation: This production-quality script demonstrates all exit best practices:
  • Defined exit codes with meaningful names
  • Consistent error handling with descriptive messages
  • Trap-based cleanup that always runs
  • Comprehensive logging
  • Dependency checking with appropriate exit codes
  • Clear success path with exit 0
  • Distinct exit codes for different failure scenarios
Real-World Application: This pattern is used in production environments for automated tasks that need to be monitored. The exit codes allow monitoring systems to:
  • Detect specific types of failures
  • Send targeted alerts to appropriate teams
  • Trigger automatic remediation for known issues
  • Generate accurate reports on backup success rates

Standard Exit Codes

Code Meaning Common Use
0 Success Command completed successfully
1 General error Catchall for general errors
2 Misuse of shell command Missing keyword, command, or permission problem
126 Command cannot execute Permission problem or command is not executable
127 Command not found Possible typo or PATH issue
128 Invalid exit argument Exit takes only integer args in range 0-255
128+n Fatal error signal "n" 130 (128+2) = terminated by Ctrl+C (SIGINT)
130 Script terminated by Ctrl+C SIGINT (signal 2)
137 Script terminated by SIGKILL kill -9 (signal 9)
143 Script terminated by SIGTERM kill (signal 15, default)
255 Exit status out of range Exit takes only integer args in range 0-255

Best Practices

1. Always Use Meaningful Exit Codes:
# Bad - uses same code for different errors if [ ! -f "$CONFIG" ]; then exit 1 fi if [ ! -w "$LOGDIR" ]; then exit 1 fi # Good - distinct codes for different errors if [ ! -f "$CONFIG" ]; then echo "Config file not found" exit 2 fi if [ ! -w "$LOGDIR" ]; then echo "Log directory not writable" exit 3 fi
2. Use trap for Cleanup:
# Always clean up resources trap 'rm -rf "$TEMP_DIR"' EXIT # Your script code here TEMP_DIR=$(mktemp -d) # ... work ...
3. Check Exit Status of Important Commands:
# Bad - ignores errors mysqldump database > backup.sql echo "Backup complete" # Good - checks status if ! mysqldump database > backup.sql; then echo "Backup failed" exit 1 fi echo "Backup complete"
4. Use Functions Instead of exit in Reusable Code:
# Bad - exits entire script function check_file() { [ -f "$1" ] || exit 1 } # Good - returns status function check_file() { [ -f "$1" ] || return 1 } # Caller can decide whether to exit if ! check_file "$CONFIG"; then echo "Config not found" exit 1 fi
Common Mistakes to Avoid:
  • Using exit in functions when you mean return
  • Not checking exit status of critical commands
  • Using same exit code for different errors
  • Forgetting cleanup code (use trap!)
  • Using exit codes outside 0-255 range
  • Relying on implicit exit status (always be explicit)

Quick Reference

Command Description
exit Exit with status of last command
exit 0 Exit with success
exit 1 Exit with general error
exit $? Exit with status of last command (explicit)
echo $? Show exit status of last command
command || exit 1 Exit if command fails
trap cleanup EXIT Run cleanup function on exit
set -e Exit on any error (use carefully!)
return N Exit function with status N
Ctrl+D Exit interactive shell (EOF)