PoorlyWritten

by olivergondza

Bash strict mode and why you should care

It has been a while since I have stumbled upon a great post of Aaron Maxwell introducing what he refers to as “Unofficial Bash Strict Mode” and started taking advantage of its benefits on everyday bases. It has even become part of my bash script template so I never (well, almost) create a new file without a strict mode header.

So, what is strict mode?

I am certain it is no easy task to design a language to serve the purpose of interactive shell and well as the one of nontrivial system programing language. However, over the years, one is constantly running into situations wishing bash would be more strict, predictable and generally resemble more of a, well, more of a programming language.

Fortunately, there are ways to get a little closer to that to protect ones sanity while working with what I (only half jokingly, though) call the most popular esoteric programming language in the world. And to the best of my knowledge, Aaron was the first one to popularize the concept of strict mode in bash.

So all in all, the strict mode is nothing more than a small bash snippet to put at the beginning of the file to alter is behavior towards the safe side. Here is his proposed strict mode declaration:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

Let me have a brief look of what it attains (see the original post for a ton of relevant details):

So this is the way to ask for extra portion of errors and constrains to handle while working with bash. Which, trust me, is a good thing. The original post gets in some lengths on how to adapt common code patterns that does not comply to work with the strict mode, and it only seldom makes the code less idiomatic while making it more resilient most of the time.

The very last line adjusts the “Internal Field Separator” to behave more intuitively around word splitting strings that contain whitespace, where spaces often behaves as undesired separators in buggy code.

Is that it?

Aaron of course is not alone striving for seamless bash experience so more strict modes has emerged. Michael Daffin has published one I really love:

#!/bin/bash
set -uo pipefail
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
IFS=$'\n\t'

It appears to be an alternation of the previous one (albeit it omits set -e), but, what’s with the trap? Traps in bash registers a (string) code snippet to be executed when script exits with declared error code, or as in the case of ERR, when a command returns non-zero code (same exceptions apply as with set -e). I find this to be a true gem when used with set -e and set -o pipefail that alone just abort the script, but does not tell you where it failed. With this, it clearly reports that script has failed (which you may miss unless you examine the exit code) stating the line number and the command that failed! This makes it fail fast, and loud.

Are we saved?

Well, not quite. While all these contribute to the effort of maintaining developer’s sanity, they come to a cost. Apart from caveats covered in Aaron’s original post, watch out for these:

Which one to use, then?

So, I ended up cultivating my own strict mode that, to this day, looks something like this:

#!/usr/bin/env bash
set -euo pipefail
trap 's=$?; echo >&2 "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR

Note that set -euo pipefail part (and its subsets) has become quite notorious and a lot of folks is using variations of that sporadically. One advantage of using consistent strict mode definition is all the scripts are behaving predictably. It is enough we have chosen to deviate from bash defaults, let’s at least be consistent in doing so.

Isn’t it time to start thinking about bash strict mode in your life?


Tags: bash, productivity