Linux Basic — Array Slicing & Copying
Array Slicing & Copying
You learned ${var:offset:length} for strings. The same syntax works on arrays — but the offset is element index, not byte. This page covers slicing, deep-copying, and merging.
Slicing — ${arr[@]:offset:length}
arr=(red green blue yellow purple orange)
echo "${arr[@]:0:3}" # red green blue (first 3)
echo "${arr[@]:2:2}" # blue yellow (2 starting at index 2)
echo "${arr[@]:3}" # yellow purple orange (from 3 to end)
echo "${arr[@]: -2}" # purple orange (last 2 — space required)
echo "${arr[@]: -3:2}" # yellow purple (2 starting 3 from end)
Note:
${arr[@]:1:2}returns 2 elements starting at index 1- Space before
-is required to disambiguate from${var:-default} - Negative offset works in Bash 4.2+
To assign the slice back to a new array, wrap in ():
first_half=( "${arr[@]:0:3}" )
second_half=( "${arr[@]:3}" )
echo "${first_half[@]}" # red green blue
echo "${second_half[@]}" # yellow purple orange
Slicing Positional Parameters
$@ and $* slice exactly the same way:
set -- a b c d e
echo "${@:2}" # b c d e (from index 2)
echo "${@:2:3}" # b c d (3 from index 2)
echo "${@: -1}" # e (last)
echo "${@:1:$#-1}" # a b c d (all but last)
Useful idiom — “command and its args”:
mycmd_with_logging() {
local cmd=$1
local args=( "${@:2}" )
echo ">>> Running: $cmd ${args[*]}"
"$cmd" "${args[@]}"
}
mycmd_with_logging echo hello world
Copying Arrays — Deep, Always
Bash has no reference semantics for arrays (without nameref). Copying means cloning every element:
src=(apple banana cherry)
# ✅ Correct deep copy — note the quoted expansion + parens
dst=( "${src[@]}" )
dst[0]=apricot
echo "${src[@]}" # apple banana cherry (unchanged)
echo "${dst[@]}" # apricot banana cherry
The quoting matters: dst=( ${src[@]} ) (unquoted) would word-split each element on $IFS, breaking entries with spaces:
src=("hello world" "foo bar")
dst_bad=( ${src[@]} ) # ❌
dst_ok=( "${src[@]}" ) # ✅
printf 'bad : %s\n' "${dst_bad[@]}"
# bad : hello
# bad : world
# bad : foo
# bad : bar
printf 'ok : %s\n' "${dst_ok[@]}"
# ok : hello world
# ok : foo bar
Copying Associative Arrays
There’s no equally clean one-liner. You need to iterate:
declare -A src=([host]=example.com [port]=8080)
declare -A dst
for k in "${!src[@]}"; do
dst[$k]=${src[$k]}
done
Bash 5.1+ has declare -A dst=( "${src[@]@K}" ) using the @K transformation, but that’s a recent addition. The loop above works everywhere.
Concatenation
Indexed arrays merge via += or explicit reconstruction:
a=(1 2 3)
b=(4 5 6)
# Concat via +=
combined=( "${a[@]}" )
combined+=( "${b[@]}" )
# Or in one shot
combined=( "${a[@]}" "${b[@]}" )
echo "${combined[@]}" # 1 2 3 4 5 6
Associative arrays don’t merge with +=. Loop the second into the first:
declare -A first=([a]=1 [b]=2)
declare -A second=([b]=20 [c]=30)
for k in "${!second[@]}"; do
first[$k]=${second[$k]}
done
# first now: [a]=1 [b]=20 [c]=30 ← "second" wins on conflicts
Passing Arrays to Functions
You can’t pass a Bash array as a single argument — only its expansion. Two clean patterns:
Pattern A — Expand and Capture
sum() {
local total=0
for n in "$@"; do
total=$((total + n))
done
echo "$total"
}
nums=(10 20 30)
sum "${nums[@]}" # 60
The function receives the elements as $1, $2, … — most idiomatic.
Pattern B — Pass by Name (nameref)
print_array() {
local -n ref=$1 # nameref to caller's array
for x in "${ref[@]}"; do
echo "[$x]"
done
}
fruits=(apple banana cherry)
print_array fruits # pass the *name*
local -n (Bash 4.3+) makes ref an alias for the caller’s variable. Useful when you need to modify the caller’s array, or pass an associative array.
A Real Example — Filter Then Slice
all_users=(alice bob carol dan eve frank)
# Keep users starting with a-c
filtered=()
for u in "${all_users[@]}"; do
[[ "${u:0:1}" == [a-c] ]] && filtered+=("$u")
done
# Take first 2 of the filtered set
top2=( "${filtered[@]:0:2}" )
echo "${top2[@]}" # alice bob
Filter via a loop into a new array, then slice — composable and clear.
Common Mistakes
| ❌ Wrong | ✅ Right | Issue |
|---|---|---|
dst=$src (no parens) |
dst=( "${src[@]}" ) |
$src is just ${src[0]} |
dst=( ${src[@]} ) (unquoted) |
dst=( "${src[@]}" ) |
unquoted re-splits on $IFS |
for x in ${arr[*]} |
for x in "${arr[@]}" |
* joins; @ separates |
assoc_copy=( "${src[@]}" ) |
iterate keys + reassign | assoc copy needs key/value loop |
${arr[@]:-1} for “last” |
${arr[@]: -1} (space) |
:- is default operator |
Next → Sorting Arrays