Free tech learning for everyone, from basics to pro

ScriptingProgramming and Application SecurityBeginner

Bash scripting basics: variables, conditions, loops and exit codes

In short: Bash is the default shell on most Linux systems and the quickest way to automate small jobs. A good script starts with a shebang and safety options, quotes its variables, checks exit codes and keeps logic in small functions.

Why engineers still write Bash

Bash (the Bourne Again SHell) is the command interpreter on almost every Linux server, jump host, firewall appliance shell and network operating system that exposes a Linux prompt. If you type the same five commands every morning, a Bash script can run them for you. It needs no extra installation, starts instantly and glues together tools that already exist, such as ping, ssh, grep and curl.

Bash is not the right tool for everything. Once a job needs data structures, API calls with JSON or careful error handling, Python is usually easier to read and test. A sensible rule is: if the script grows past about 100 lines, consider rewriting it in Python.

Anatomy of a Bash scriptShebang#!/usr/bin/env bashSafety optionsset -euo pipefailVariablesnames, arrays, inputLogicif, case, loopsFunctionsreusable blocksExit code0 = success
Figure 1. A typical Bash script reads top to bottom: interpreter line, safety options, data, logic, functions and a clear exit code.

Your first script

Save the following as check_hosts.sh, make it executable with chmod +x check_hosts.sh and run it with ./check_hosts.sh.

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

SITE="branch-01"
HOSTS=("192.168.10.1" "192.168.10.2" "192.168.10.10")

for h in "${HOSTS[@]}"; do
  if ping -c 1 -W 2 "$h" > /dev/null 2>&1; then
    echo "$SITE $h up"
  else
    echo "$SITE $h DOWN"
  fi
done
  • Shebang: the first line tells the system which interpreter to use. /usr/bin/env bash finds Bash wherever it is installed.
  • set -euo pipefail: stop on any failing command (-e), treat unset variables as errors (-u) and make a pipeline fail if any part fails (pipefail).
  • Variables: assign with no spaces around =; read with "$NAME". Arrays use "${ARRAY[@]}".
  • Redirection: > /dev/null 2>&1 throws away normal output and errors, because you only care about the exit code.

Conditions, exit codes and functions

Every command returns an exit code: 0 means success, anything else means failure. if simply runs a command and checks that code. For comparisons you use the test brackets [[ ... ]].

backup_dir="/var/backups/net"

log() { echo "$(date '+%F %T') $*"; }

if [[ ! -d "$backup_dir" ]]; then
  log "creating $backup_dir"
  mkdir -p "$backup_dir"
fi

case "${1:-status}" in
  status) log "status requested" ;;
  clean)  find "$backup_dir" -name '*.cfg' -mtime +30 -print ;;
  *)      echo "usage: $0 [status|clean]"; exit 2 ;;
esac

$1 is the first argument, and ${1:-status} supplies a default if none was given. Ending with a meaningful exit code lets schedulers and other scripts know whether your job worked.

Quoting and common mistakes

Quoting in BashDouble quotes "..."Expands $VARExpands $(command)Keeps spaces togetherUse by defaultSingle quotes '...'Nothing expandsText taken literallyGood for regex, awkCannot hold a single quote
Figure 2. Double quotes expand variables but keep words together; single quotes stop all expansion.
MistakeWhat goes wrongFix
name = valueBash tries to run a command called nameNo spaces: name=value
Unquoted $fileFile names with spaces split into several wordsAlways write "$file"
No set -eScript carries on after a failed stepUse set -euo pipefail and handle expected failures explicitly
Windows line endingsbad interpreter errorsSave with LF endings, or run dos2unix

Tip: run shellcheck script.sh before you share a script. This free linter catches quoting bugs and risky patterns that are easy to miss by eye.

Practice

What does an exit code of 0 mean?
The command succeeded. Any non-zero value means it failed, and if uses this to decide which branch to run.
Why write "$host" rather than $host?
Quoting keeps the value as one word, so spaces or special characters in the value do not break the command.
What does set -u protect you from?
Using a variable that was never set, for example a typo in its name, which would otherwise silently expand to an empty string.

Key takeaways

  • Start scripts with a shebang and set -euo pipefail.
  • Quote variables with double quotes; use single quotes for literal text.
  • if checks exit codes; 0 means success.
  • Use functions and arguments with defaults to keep scripts reusable.
  • Lint with shellcheck and move to Python when logic grows.
Tracks your learning on this device only. See your progress on the learning paths.

About the author

Mohamed Fayas

Founder of FM TecHub. CCNA certified, with a BTEC HND in Network Engineering, and BEng (Hons) in Computer Networking and Cloud Security. He writes practical, plain-English guides on networking, security and cloud.

More about the author

Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.

Stay up to date by email

New notes, tools and security news from FM TecHub, explained simply. Free.

We only use your email to send these updates. See our privacy policy.