Um momento
0x100Lesson 17 of 18

Write robust scripts

Make scripts fail safely with strict mode, clean up with traps, parse options with getopts, and debug.

30 min 7-question quiz 2 code exercises
By the end of this lesson you can
  • Explain what set -euo pipefail does, and its gotchas
  • Report errors to stderr, guard dangerous expansions and clean up with trap
  • Parse options with getopts and debug with bash -x and ShellCheck

By default, Bash carries on after a command fails. That’s friendly at the prompt and dangerous in a script: if cd /backups fails, the rm -rf * on the next line runs wherever you happen to be. Many scripts start with strict mode:

#!/usr/bin/env bash
set -euo pipefail
OptionEffect
-e (errexit)exit as soon as a command fails
-u (nounset)treat an unset variable as an error instead of an empty string
-o pipefaila pipeline fails if any stage fails, not just the last
pipefail.sh
1grep reactor /no/such/file 2>/dev/null | sort
2echo "without pipefail, the pipeline's status is $? (sort's)"
3set -o pipefail
4grep reactor /no/such/file 2>/dev/null | sort
5echo "with pipefail, it's $? (grep's)"
Output
without pipefail, the pipeline's status is 0 (sort's)
with pipefail, it's 2 (grep's)

Defensive habits

  • Quote every expansion: "$file", "${files[@]}", "$(cmd)".
  • Guard dangerous paths: rm -rf "${build_dir:?}/"* stops the script with an error if build_dir is empty, instead of deleting from /.
  • End options with --: rm -- "$file" stays safe even if a file is named -rf.
  • Errors go to stderr, with a non-zero exit: a small die function keeps it tidy.
  • Clean up with trap: create temporary files with mktemp and remove them in an EXIT trap.
  • Check inputs early and print a usage message.
defensive.sh
1set -euo pipefail
2die() { echo "error: $*" >&2; exit 1; }
3work=$(mktemp -d)
4trap 'rm -rf "$work"; echo "cleaned up"' EXIT
5
6target="${1:-}"
7[[ -n $target ]] || target="kestrel"          # a default instead of dying
8printf '%s\n' alpha beta > "$work/list.txt"
9count=$(wc -l < "$work/list.txt")
10echo "processed $count items for $target"
11[[ -d $work ]] && echo "temp dir exists until exit"
Output
processed 2 items for kestrel
temp dir exists until exit
cleaned up

Try it

Review this backup script

A crewmate wrote this nightly backup script. Click every line that could cause trouble, then check what you missed.

backup.sh (1 of 1)Flags found 0/0

Click every part that looks suspicious. There are 6.

#!/usr/bin/env bash backup_dir=$1 cd $backup_dir rm -rf $backup_dir/tmp/* for f in $(ls *.log); do cp $f /archive done echo "Backup complete"

Options with getopts

Real tools take options: report -v -n 5 station.log. The getopts built-in parses short options for you. The option string lists the letters; a colon after a letter means it takes a value (in OPTARG); a leading colon lets you handle errors yourself:

getopts.sh
1set -- -v -n 2 Kestrel        # as if run: ./hail.sh -v -n 2 Kestrel
2usage() { echo "usage: hail [-v] [-n count] name" >&2; exit 2; }
3verbose=false count=1
4while getopts ":vn:h" opt; do
5  case $opt in
6    v) verbose=true ;;
7    n) count=$OPTARG ;;
8    h) usage ;;
9    :) echo "-$OPTARG needs a value" >&2; usage ;;
10    ?) echo "unknown option -$OPTARG" >&2; usage ;;
11  esac
12done
13shift $(( OPTIND - 1 ))          # drop the options; "$@" is now the rest
14name=${1:?missing name}
15$verbose && echo "count=$count name=$name"
16for (( i = 0; i < count; i++ )); do echo "Hailing $name"; done
Output
count=2 name=Kestrel
Hailing Kestrel
Hailing Kestrel

Debugging

  • bash -n script.sh checks the syntax without running anything.
  • bash -x script.sh, or set -x inside it, prints every command after expansion, prefixed with + - you see exactly what ran. set +x turns it off again.
  • ShellCheck is a linter that catches unquoted variables, useless cats, $(ls) loops and hundreds of other mistakes. Most editors can run it as you type. Use it on every script.
trace.sh
1deck="deck 4"
2set -x
3ls $deck 2>/dev/null
4set +x
5echo "the trace (on stderr) shows ls received two arguments"
Output
the trace (on stderr) shows ls received two arguments

Key takeaways

  • set -euo pipefail stops on failures, unset variables and broken pipelines - mind its gotchas.

  • Quote everything, guard with ${var:?}, use --, and send errors to stderr with a non-zero exit.

  • trap ... EXIT cleans up; getopts parses options; shift $((OPTIND - 1)) leaves the arguments.

  • Debug with bash -x and lint every script with ShellCheck.

Lesson quiz

7 questions · pass with 5 correct · up to 50 XP

Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.

Practice: write Bash scripts

Write a script in the editor and run it for real against sample input. Each run gets a fresh Linux sandbox with Bash 5.2 and the GNU tools on Wandbox, a free public service - so experiment freely, even with rm. Your script and test input are sent there.

Exercise 1

Parse the options

+25 XP

The starter turns the line on stdin into the script’s arguments. Use getopts to support:

  • -n COUNT - how many times to greet (default 1)
  • -u - upper-case the name
  • -v - first print count=N name=NAME (before any upper-casing)

then a name argument. Print Hello, NAME! COUNT times. For example, -u -n 2 Ada prints Hello, ADA! twice.

  • -n 2 Kestrel
  • -u -n 2 Ada
  • -v Grace
  • Just a name
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Exercise 2

Clean up after yourself

+25 XP

The starter makes a temporary directory and processes some samples, but never cleans up. Add an EXIT trap that calls cleanup, and write cleanup so that it prints cleaning up N files (the number of files in the temp directory), deletes the directory, and then prints removed: yes if it’s gone.

1working...
2result: 3 samples
3cleaning up 3 files
4removed: yes
  • Cleans up
script.sh
Loading editor…

Your script runs with Bash 5.2 and GNU tools on Wandbox, a free public service, in a fresh sandbox each time. Your script and test input are sent to that service.

Questions about this lesson

Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.

Loading posts…

Gostou da aula? 😆👍
Apoie nosso trabalho com uma doação: