TIL: set -u in bash

Trying a new Today I learned section… Today, I learned about set -u, which will fail a bash script if we try to use a variable that's not defined. No more deleting someone's files because a variable was not defined. Demo:

#!/bin/bash
set -u

GREETING=Hello

#oups I misspelt that
echo ${GREETINGS} world

If you run this:

$ ./script
./script: line 7: GREETINGS: unbound variable

That makes testing whether a variable is set slightly more complicated:

set -u
if [[ -z ${GREETINGS} ]]; then
    # oups we never get here, the previous line fails with "unbound variable"
    GREETINGS=$( ....compute greetings ...)
fi

Instead:

set -u
if [[ ! -v GREETINGS ]]; then
    GREETINGS=$( ... compute greetings ... )
fi

Where -v is a Bash Conditional Expression described thusly:

-v varname
True if the shell variable varname is set (has been assigned a value).

I suppose the actually equivalent test would be: \[\[ ! -v GREETINGS ]] || \[\[ -z ${GREETINGS} ]], to check whether the variable is not only set but also non-null.

I've always used set -e and set -o pipefail in shell scripts. The first will exit the entire script if any command (more specifically: any pipeline fails). However by default, quoting the manual: the "exit status of a pipeline is the exit status of the last command in the pipeline", meaning the following succeeds:

$ false | true
$ echo $?
0

But, if "pipefail is enabled, the pipeline's return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully", so:

$ set -o pipefail
$ false | true
$ echo $?
1

TL;DR: Use set -euo pipefail at the top of your bash scripts. Julia Evans agrees.

Auteur : Frédéric Perrin

Date : lundi 1 avril 2024

Sauf mention contraire, les textes de ce site sont sous licence Creative Common BY-SA.

Ce site est produit avec des logiciels libres 100% élevés au grain et en plein air.