Shell by Example: Arrays Bash

Bash

Arrays in Bash let you store multiple values in a single variable. Note: Arrays are a Bash feature and are not available in POSIX sh.

This example creates an array with four elements.

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")

Bash

Access elements using index (0-based).

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
echo "First fruit: ${fruits[0]}"
echo "Second fruit: ${fruits[1]}"
Output:
First fruit: apple
Second fruit: banana

Bash

Get all elements with [@] or [*].

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
echo "All fruits: ${fruits[*]}"
Output:
All fruits: apple banana cherry date

Bash

Get the number of elements.

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
echo "Number of fruits: ${#fruits[@]}"
Output:
Number of fruits: 4

Bash

Add an element to the end.

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
fruits+=("elderberry")
echo "After adding: ${fruits[*]}"
Output:
After adding: apple banana cherry date elderberry

Bash

Set a specific index.

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
fruits[1]="blueberry"
echo "After replacing: ${fruits[*]}"
Output:
After replacing: apple blueberry cherry date

Bash

Loop over array elements.

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
for fruit in "${fruits[@]}"; do
    echo "  - $fruit"
done
Output:
  - apple
  - banana
  - cherry
  - date

Bash

Get array indices with ${!array[@]}, and loop with indices.

Edit
#!/bin/bash
fruits=(apple banana cherry)

echo "Indices: ${!fruits[*]}"

echo "With indices:"
for i in "${!fruits[@]}"; do
    echo "  [$i] = ${fruits[$i]}"
done
Output:
Indices: 0 1 2
With indices:
  [0] = apple
  [1] = banana
  [2] = cherry

Bash

Array slicing: ${array[@]:start:length}

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
echo "First three: ${fruits[*]:0:3}"
echo "From index 2: ${fruits[*]:2}"
Output:
First three: apple banana cherry
From index 2: cherry date

Bash

Check if array is empty.

Edit
#!/bin/bash
empty_array=()
if [ ${#empty_array[@]} -eq 0 ]; then
    echo "Array is empty"
fi
Output:
Array is empty

Bash

Create array from command output.

Edit
#!/bin/bash
touch file1.txt file2.txt file3.txt

# create array from command output using mapfile
mapfile -t files < <(find . -maxdepth 1 -type f -print)
echo "Shell files: ${files[*]}"
Output:
Shell files: ./file3.txt ./file2.txt ./file1.txt

Bash

Delete an element (leaves a gap in indices).

Edit
#!/bin/bash
fruits=("apple" "banana" "cherry" "date")
unset 'fruits[2]'
echo "After unset [2]: ${fruits[*]}"
echo "Indices now: ${!fruits[*]}"
Output:
After unset [2]: apple banana date
Indices now: 0 1 3

Bash

Bash provides associative arrays (hash maps) using declare -A.

Edit
#!/bin/bash
declare -A user
user[name]="Alice"
user[email]="alice@example.com"
user[age]="30"

echo "Name: ${user[name]}"
echo "Email: ${user[email]}"
Output:
Name: Alice
Email: alice@example.com

Bash

Iterate over associative array keys:

Edit
#!/bin/bash
declare -A user
user[name]="Alice"
user[email]="alice@example.com"
user[age]="30"

for key in "${!user[@]}"; do
    echo "$key: ${user[$key]}"
done
Output:
email: alice@example.com
age: 30
name: Alice

Bash

Check if a key exists:

Edit
#!/bin/bash
if [[ -v user[name] ]]; then
    echo "name key exists"
fi

Bash

Bash provides mapfile (or readarray) to read lines from a file or command into an array:

Edit
#!/bin/bash
mapfile -t lines </etc/passwd
for line in "${lines[@]}"; do
    echo "$line"
done
Output:
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin

Bash

Read only first 5 lines:

Edit
#!/bin/bash
mapfile -t -n 5 lines </etc/passwd
for line in "${lines[@]}"; do
    echo "$line"
done
Output:
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

Bash

Read from a command using process substitution:

Edit
#!/bin/bash
mapfile -t users < <(cut -d: -f1 /etc/passwd | head -5)
echo "Users: ${users[*]}"
Output:
Users: root bin daemon lp sync

Bash

Read with a specific delimiter:

Edit
#!/bin/bash
mapfile -t -d ':' fields <<<"a:b:c:d"
echo "Fields: ${fields[*]}"
Output:
Fields: a b c d

« Functions | Here Documents »