Skip to main content

Bash Scripting

The one-sentence version

Bash runs commands top to bottom and, by default, keeps going after failures — so the most important thing you can learn is how to make a script stop when something breaks.

The shebang

The first line tells the kernel which interpreter to use.

#!/usr/bin/env bash
FormBehaviour
#!/bin/bashBash at a fixed path. Fine on Linux, missing on some systems.
#!/usr/bin/env bashFirst bash on PATH. More portable.
#!/bin/shThe system POSIX shell — not necessarily Bash

On Debian and Ubuntu, /bin/sh is dash, not Bash. A script with #!/bin/sh that uses arrays or [[ ]] will fail there, often with a confusing error. Declare bash if you use Bash features.

Strict mode

Default Bash behaviour is dangerous in automation: a failing command is ignored and execution continues, so a script can "succeed" having done half its work.

#!/usr/bin/env bash
set -euo pipefail
FlagWithout it
-eA failing command is ignored and the script continues
-uA typo'd variable expands to empty string, silently
-o pipefailA pipeline reports success if the last command succeeded

pipefail is the least obvious and matters most in DevOps:

# Without pipefail this exits 0 — grep succeeded, so the failure is hidden
curl -f https://api.example.com/health | grep -q ok

set -u catching typos is worth dwelling on. Without it, this deletes the wrong thing:

rm -rf "$BUILD_DIR/" # if BUILD_DIR is unset, this is rm -rf /

Escape hatch for a command allowed to fail:

grep -q pattern file || true

Expansion and substitution

Two different mechanisms that look similar.

name=$(hostname) # command substitution: run it, capture stdout
echo "${name}" # parameter expansion: read the variable

Prefer $(...) over backticks — it nests and reads better. Useful parameter expansions:

FormResult
${var:-default}default when var is unset or empty
${var:?message}Abort with message when unset
${#var}Length of the value
${var%.txt}Strip shortest matching suffix

Always quote your expansions. Unquoted $var is word-split on whitespace, which is how a filename with a space becomes two arguments.

Cleanup with trap

trap runs a handler when the shell exits, so temporary state is cleaned up whether the script succeeded, failed, or was interrupted.

#!/usr/bin/env bash
set -euo pipefail

workdir=$(mktemp -d)
cleanup() { rm -rf "$workdir"; }
trap cleanup EXIT

# ... work in "$workdir" ...

EXIT fires on normal exit, on set -e abort, and on SIGTERM. Pairing set -e with a trap ... EXIT is what makes a script safe to run unattended.

Questions

Loading questions…

Learn more