set -e to make the current shell exit if a command run within it exists unsuccessfully and
set +e to disable the behavior
Whether you love or hate Bash, it’s hard to deny the raw beauty of Bash scripting. Portable (mostly!), surprisingly concise, and perfect for environments where you don’t have (or want) the overhead of a higher level language, Bash can be a wonderful go-to when hopping between *nix environments. I’ve recently been using it to write some common docker
ENTRYPOINTs for running a few services in development and ran into an issue where I wanted the behavior of
set -e enabled, disabled, and re-enabled in same script.
set -e, makes the current shell exit if any command run inside of it exits with a non
0 status. While this may seem harsh at first, it’s a great way to ensure that an error in one step of you script doesn’t lead to undefined behavior elsewhere; it’s much better to stop a script when you fail to connect to a database than have it merrily chug along trying to copy from nowhere.
Because Bash is always testing you, it would be too easy to have something like
exit_on_errors(false) what we do have is the wonderful
set command. While
help set is actually quite helpful, it’s easy to miss the following line:
Using + rather than - causes these flags to be turned off.
In my case, I had something like the following pseudocode:
#!/bin/bash set -e # wait for a service to spin up; exit 1 after 30 seconds wait-for-my-service-or-exit http://my-service/healthcheck # ensure local data uses most recent copy command_to_check_if_data_current DATA_CURRENT=$? if [ "$DATA_CURRENT" -eq 1 ]; then # update the data from from my-service # if we don't have our file download_data_from_my_service fi
But I was never getting to my
if statement because
command_to_check_if_data_current would exit with a
1 if the data didn’t exist in the container. This was because I was using
set -e in my script to protect against unknown failures and the
command_to_check_if_data_current command (which called
exit 1) would exit the parent script regardless of whether or not it was called in a subshell.
The solution was to use a combination of
set +e and
set -e to wrap my call to
#!/bin/bash set -e # ensure my-service is up wait-for-my-service-or-exit http://my-service/healthcheck set +e # ensure local data uses most recent copy command_to_check_if_data_current DATA_CURRENT=$? set -e if [ "$DATA_CURRENT" -eq 1 ]; then # update the data from from my-service download_data_from_my_service fi
Not cleanest code perhaps, but it allows for isolating known failures and still ensuring that unknown errors stop your script when they should.