DevOps Peach

Linux · Bash Scripting · Lesson 2 of 5

Variables, expansion, and arrays

Preserve data boundaries while Bash expands values.

Practice interactively →

Variables and quoting

$name expands a variable; ${name} makes the boundary explicit in text such as ${name}_log. Quote "$name" so a value containing spaces stays one argument and glob characters are not unexpectedly expanded. Single quotes keep text literal: '$HOME' prints the five characters, while "$HOME" prints your home path.

A plain variable exists only in the current shell. export APP_ENV=prod turns it into an environment variable that child processes inherit. env lists the environment; common entries are HOME, USER, PATH, and PWD. readonly protects a value and unset name removes it.

flowchart LR
  L["Command line<br/>cp $src $dst"] --> PE["Parameter expansion<br/>${name}"]
  PE --> CS["Command substitution<br/>$(date +%F)"]
  CS --> WS["Word splitting<br/>unquoted only"]
  WS --> GL["Globbing<br/>* ? [ ]"]
  GL --> RUN["Run the command<br/>final arguments"]
  %% step L: Bash reads the line before running anything.
  %% step PE: Variables are replaced by their values.
  %% step CS: $( ) runs a command and inserts its output.
  %% step WS: Unquoted results are split on spaces, so "my file.txt" would become two words.
  %% step GL: Then wildcards are expanded to filenames.
  %% step RUN: Quoting "$var" stops splitting and globbing so the data keeps its boundaries.

Command and arithmetic substitution

today=$(date +%F) stores a command's output. $((count + 1)) evaluates integer arithmetic; Bash has no built-in floating point, so tools such as bc or awk handle decimals. The older backtick form `date` still works but nests poorly.

Brace and parameter expansion

Brace expansion generates text before a command runs: {1..3} becomes 1 2 3. It does not read runtime variables, so {1..$n} does not make a dynamic range. Parameter expansion operates on values: ${food:-Cake} supplies a default without assigning it, while ${food:=Cake} also assigns the default.

Expansion Result with name=John
${#name} 4 (length)
${name/J/j} john
${name:0:2} Jo
${name::-1} Joh

Prefix and suffix removal is handy for file names: with file=backup.tar.gz, ${file%.gz} gives backup.tar, ${file%%.*} gives backup, and ${path##*/} keeps only the part after the last slash, like basename.

Arrays

fruits=(Apple Banana Orange)
fruits+=(Watermelon)
printf '%s\n' "${fruits[@]}"
printf '%s\n' "${#fruits[@]}"

Quoted ${fruits[@]} preserves each element as a separate argument. ${#fruits[@]} is the element count; ${#fruits} is only the length of element zero. In modern Bash, a negative index such as ${fruits[-1]} selects the final element.

Bash 4 and later also support associative arrays keyed by strings:

declare -A port=([http]=80 [https]=443)
echo "${port[https]}"
echo "${!port[@]}"   # the keys

Practice interactively →