Making a script robust

Bash's strict mode, trap for cleaning up, mktemp, errors on the error output with an exit status, idempotence and shellcheck: knowing where a script stops and what it leaves behind.

  • 30 cards
  • 5 topics
  • Free

The outline, topic by topic

Each topic is weighted the way the exam weighs it.

  1. Concepts
  2. Questions
  3. Sort and link
  4. Scenarios
  5. Understand

A few cards from the deck

  • Strict mode: what is it?

    Show the answer

    Three Bash settings that make a script fail instead of carrying on: set -e (stop on error), set -u (undefined variable) and set -o pipefail (failure in a pipe).

    Without it, cd /nope fails and the script goes on with rm -rf * in the wrong folder. But set -e ignores failures in a test (if, &&, ||): it does not replace checking for foreseeable cases.

  • What happens when this script runs?

    ```bash
    set -e
    cd /nope
    echo fini
    `

    • It stops on the cd error, status 1, without printing fini
    • It prints the cd error, then fini, and returns status 0
    • It prints fini with no error, since cd only changes folder
    • It asks for confirmation before carrying on with the script
    Show the answer

    It stops on the cd error, status 1, without printing fini

    cd /nope fails (“No such file or directory”); under set -e, any simple command's failure stops the script with that command's status, here 1. Without set -e, fini would be printed.

  • With set -u, reading a variable that was never defined stops the script with the message “unbound variable”; without it, Bash replaces it with ….

    Show the answer

    With set -u, reading a variable that was never defined stops the script with the message “unbound variable”; without it, Bash replaces it with an empty string.

    It is what catches the typo rm -rf "$dosier"/*: without -u, the empty variable gives /*. A deliberate default avoids the stop: ${1:-default}.

  • pipefail: what is it?

    Show the answer

    A setting that gives a pipe the status of the last command that failed, instead of that of its last command alone.

    false | true returns 0 by default: the failure of false goes unnoticed. With set -o pipefail, it returns 1, and set -e can then stop the script.

  • Every night, the /tmp folder grows by 2 GB until the disk is full. The processing script creates a file with tmp=$(mktemp) and deletes it on its last line, but sometimes stops on an error. Which fix is right?

    • Add trap 'rm -f "$tmp"' EXIT just after mktemp
    • Put the rm -f at the start of the script rather than the end
    • Replace mktemp with a fixed name in /tmp
    • Add set -u so the script stops earlier
    Show the answer

    Add trap 'rm -f "$tmp"' EXIT just after mktemp

    When the script stops before its last line, the final rm never runs and the file stays. The EXIT trap runs however the script ends. A rm at the start would delete a file that does not exist yet.