Useful Flags to Set on Every Shell Script

When writing shell scripts, it's a good idea to start with a standard set of flags that help make the scripts more robust and easier to debug. Here are some common flags that I include at the top of every shell script:

#!/bin/bash

The first line specifies which interpreter to use to run the script. #!/bin/bash will ensure the Bash shell is used. This allows the script to use Bash features and syntax not available in other shells like dash or sh. You could also adapt this to ZSH or other shells if you prefer.

set -euo pipefail

This set of flags helps catch errors early and makes the script behave in a predictable way:

  • -e exits immediately if any simple command fails with a non-zero status. This avoids executing further commands that may have unintended consequences.

  • -u treats unset variables as an error when substituting, rather than substituting with an empty string. This helps catch typos in variable names.

  • -o pipefail returns a non-zero exit code if any command in the pipeline fails, not just the last one. This helps catch errors in a pipeline.

set -x

Including set -x will print each command before executing it. This is extremely useful for debugging scripts to see exactly what commands are run. It's common to wrap it in a check for a DEBUG environment variable so it's easy to enable debugging:

if [ -n "$DEBUG" ]; then set -x; fi

Error Handling

Adding some boilerplate error handling can make scripts more resilient. Trapping errors with ERR and EXIT traps lets you perform cleanup tasks:

trap 'echo An error occurred; exit 1' ERR

trap 'echo Exited!; exit 1' EXIT

For more robust handling, saving the exit code to a variable allows inspecting the specific failure:

err=$?
if [ $err -ne 0 ]; then
  echo "Error with exit code $err" >&2
  exit $err
fi

Starting scripts with a standard header like this makes adding flags easier. The keys are -e for early exits, -u to catch typos, -o pipefail for pipeline errors, -x debugging, and ERR/EXIT traps for cleanup. Following these best practices will produce more reliable and debuggable Bash scripts.

Complete Example

Here's a complete example that you can copy and paste as a starting template:

#!/bin/bash

# Exit on any failure
set -eo pipefail

# Debug mode
if [ -n "$DEBUG" ]; then
  set -x
fi

# Handle errors
trap 'echo "Error: $? at line $LINENO" >&2' ERR


# Script logic
function cleanup() {
  # Commands to clean up resources
  echo "Cleaning up..."
}

# Cleanup before exit
trap 'cleanup' EXIT

echo "Starting script..."

# Rest of script...

echo "Script finished!"

Enjoy.