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.
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"
doneEach 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"
doneUnquoted 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]}"
doneFor 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?
"${array[@]}".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.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.