I write a fair amount of BASH scripts in the course of my daily work. Even more now that I’ve been doing lots of work with CI pipelines; BASH tends to be a really natural choice for those simple scripts.
Most of the time these scripts are running in a totally automated manor so robust debugging and logging needs to be written into the script directly. In the olden days I would have written a METRIC TON of echo statements to act as print statements… there are still places where I might do that but much less so today and that’s because I leverage these handy little builtin features of BASH called subshell options.
There are a whole bunch of subshell options but I’m really referring to just two of them that I use most frequently:
set -x set -e
These two little beauties do a lot for very little effort.
What does ‘set -e’ do?
If you put the ‘set -e’ command at the top of your script, if ANY of your commands fail or exit with a bad return code the script will stop in it’s tracks. Using the ‘set -e’ will save you a significant amount of time in allowing your scripts to fail more gracefully.
Here’s an example:
#!/bin/bash set -e echo "This command works." echo "This command doesn't" > /root/output.txt echo "This other command works but you won't see it."
You won’t see the third piece of output because the 2nd command will fail due to a lack of permissions. This failure will cause the script to stop before executing the third command which is unlike how the script would normally run which is to say that it would push on blindly unless you wrote-in error checking.
Or said a little bit differently, ‘set -e’ will:
Exit immediately if a pipeline (see Pipelines), which may consist of a single simple command (see Simple Commands), a list (see Lists), or a compound command (see Compound Commands) returns a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a
untilkeyword, part of the test in an
ifstatement, part of any command executed in a
||list except the command following the final
||, any command in a pipeline but the last, or if the command’s return status is being inverted with
!. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on
ERR, if set, is executed before the shell exits.
This option applies to the shell environment and each subshell environment separately (see Command Execution Environment), and may cause subshells to exit before executing all the commands in the subshell.
If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.
The benefit of ‘set -e’ is relatively easy to see. Scripts which are much shorter and to the point and require less catching of return-codes throughout the course of execution.
What does ‘set -x’ do?
‘set -x’ is another gem. I found out about ‘set -x’ much later but it’s also quite useful. ‘set -x’ will essentially print each command to standard out as it is executed. This is exceedingly useful for debugging CI pipelines and all manor of scripts where you need verbose logging at a moment’s notice.
Try running this script to see what it does:
#!/bin/bash set -x echo "some command" echo "The time is $(date)" > /dev/null
You’ll see now the power of this option. It will actually display every command it runs right in front of you even ones you wouldn’t normally see because they’re being redirected etc. This is super handy when commands are executing with variables and being looped over. You may not otherwise see what command number 10 in your loop of 20 items looked like.
With our Powers Combined…
My last tidbit on these commands is that they can be used together. Often I’ll add ‘set -e’ along with ‘set -x’ to maximize the value I get out of quick scripts. You can also disable them with the opposite options… i.e. disable ‘set -e’ with ‘set +e’