Chapter 07Lesson 03~80 minutes

Safe Array Expansion and Quoting

Arrays solve boundary problems only if you expand them correctly. The most important Bash array rule is simple: quoted `"${array[@]}"` preserves one element per argument. Almost every unsafe alternative changes the shape of the data.

BeginnerArrays & dataHands-on lab

Learning objectives

By the end of this lesson

  • Distinguish ${array[@]} from ${array[*]}.
  • Explain quoted versus unquoted expansion.
  • Handle empty arrays safely.
  • Forward array elements as command arguments.
  • Avoid string-joining patterns that destroy boundaries.

1. Quoted @ is the default safe expansion

items=("api gateway" "worker" "*.yaml")

for item in "${items[@]}"; do
  printf 'item=<%s>\n' "$item"
done

Each element remains one shell word, regardless of spaces or wildcard characters.

2. Quoted * joins elements

items=("api gateway" "worker" "cache")
IFS=,
joined="${items[*]}"
printf 'joined=%s\n' "$joined"

Quoted "${array[*]}" creates one word by joining elements with the first character of IFS. Use it only when joining is explicitly the goal.

3. Unquoted array expansion reintroduces splitting and globbing

items=("api gateway" "*.log")

# Fragile:
for item in ${items[@]}; do
  printf 'item=<%s>\n' "$item"
done
Boundary loss

Unquoted array expansion can split elements containing whitespace and expand wildcard characters against the filesystem.

4. Execute command arrays with quoted @

command=(
  printf
  'service=%s env=%s\n'
  "api gateway"
  "staging blue"
)

"${command[@]}"

The first array element becomes the command name; the remaining elements become exact arguments.

5. Append one array to another safely

base=(--fail --silent)
extra=(--header "X-Mode: staging")

args=("${base[@]}" "${extra[@]}")

printf '<%s>\n' "${args[@]}"

6. Empty arrays should expand to zero arguments

args=()

run() {
  printf 'argc=%d\n' "$#"
}

run "${args[@]}"

Quoted "${args[@]}" on an empty array supplies zero arguments. This is exactly what command construction usually needs.

7. Quote individual element expansions too

files=("release candidate.tar.gz")

file=${files[0]}
printf 'file=<%s>\n' "$file"

cp -- "${files[0]}" destination/

Using -- before path operands protects against filenames that begin with a dash when the target command follows this convention.

8. Expand indexes separately from values

values=([2]="api" [7]="worker")

for index in "${!values[@]}"; do
  printf 'index=%s value=%s\n' "$index" "${values[$index]}"
done

For associative arrays, the same syntax returns keys rather than numeric indexes.

9. Joining for display is different from forwarding

services=("api gateway" "worker" "cache")

printf 'display: %s\n' "$(IFS=,; printf '%s' "${services[*]}")"

# Forwarding remains:
some_command "${services[@]}"

Human-readable serialization and machine-safe argument forwarding are different operations. Do not substitute one for the other.

10. printf can inspect arrays without losing boundaries

items=("api gateway" "worker" "cache*")

printf 'count=%d\n' "${#items[@]}"
printf 'item=%q\n' "${items[@]}"

%q is useful for diagnostics because it shows a shell-reusable representation without changing the underlying array.

11. Pass array data to functions as ordinary arguments

print_items() {
  local item
  for item in "$@"; do
    printf '<%s>\n' "$item"
  done
}

items=("api gateway" worker cache)
print_items "${items[@]}"

For complex shared array mutation, Bash also offers name references via declare -n, but explicit arguments are often easier to understand and test.

12. Hands-on lab: safe kubectl-style argument vector

mkdir -p "$HOME/devops-academy/bash/chapter07/lesson03"
cd "$HOME/devops-academy/bash/chapter07/lesson03"

namespace="team blue"
selector="app=api gateway"

args=(
  get
  pods
  --namespace "$namespace"
  --selector "$selector"
)

printf 'argument count=%d\n' "${#args[@]}"
printf 'arg=%q\n' "${args[@]}"

# Demonstrate exact forwarding without requiring kubectl:
printf 'SIMULATED kubectl'
printf ' %q' "${args[@]}"
printf '\n'

Verification checklist

13. Knowledge check

Question 1. What is the safest general expansion for all array elements?

Question 2. What does quoted "${array[*]}" do?

Question 3. What happens when an empty array is expanded with quoted @?

Question 4. Why is unquoted array expansion dangerous?

14. Summary

Arrays preserve boundaries only when expansions preserve them. Use quoted "${array[@]}" for iteration and forwarding, quoted "${array[*]}" only for intentional joining, and avoid unquoted expansions in production code.

15. Further reading

  • GNU Bash Reference Manual — Arrays.
  • GNU Bash Reference Manual — Shell Parameter Expansion.
  • GNU Bash Reference Manual — Word Splitting and Filename Expansion.
  • ShellCheck documentation — array quoting.
Next lesson

Reading Command Output into Arrays with mapfile

Continue Chapter 7 by building a safer model for structured shell state.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.