Skip to content
Laviyx Learn

Try

Linux and Bash course menu

Bash Arrays and String Manipulation

Use indexed and associative arrays in Bash, loop over them safely, and handle strings with parameter expansion: length, substrings, replacing text and trimming file extensions.

Intermediate7 min readUpdated
On this page

Arrays#

An array holds a list of values. Brackets create one, with spaces between items:

Terminal
fruits=("apple" "green mango" "kiwi")
echo "${fruits[0]}"
echo "${fruits[1]}"
echo "Count: ${#fruits[@]}"
Output
apple
green mango
Count: 3

Numbering starts at 0. Add to an array with +=:

Terminal
fruits=("apple" "kiwi")
fruits+=("guava")
echo "${fruits[@]}"
echo "${fruits[@]:1:2}"
Output
apple kiwi guava
kiwi guava

${fruits[@]:1:2} takes two items, starting at index 1.

Loop over an array#

Quote it as "${array[@]}", so that items with spaces stay together:

Terminal
servers=("web 1" "web 2")
for s in "${servers[@]}"; do
  echo "Checking $s"
done
Output
Checking web 1
Checking web 2

Fill an array from a command#

mapfile reads lines into an array, safely:

Terminal
printf 'one\ntwo\nthree\n' > items.txt
mapfile -t items < items.txt
echo "${#items[@]} items, last is ${items[-1]}"
Output
3 items, last is three

Associative arrays#

These use text keys, like a dictionary. They must be declared with declare -A:

Terminal
declare -A port
port[web]=80
port[ssh]=22
echo "ssh uses ${port[ssh]}"
for name in "${!port[@]}"; do
  echo "$name ${port[$name]}"
done | sort
Output
ssh uses 22
ssh 22
web 80

"${!port[@]}" gives the keys. They come in no fixed order, so we sort them.

This is useful for counting:

Terminal
declare -A seen
for word in red blue red red; do
  ((seen[$word]++))
done
echo "red=${seen[red]} blue=${seen[blue]}"
Output
red=3 blue=1

String length and substrings#

Terminal
text="Laviyx Learn"
echo "${#text}"
echo "${text:0:6}"
echo "${text: -5}"
Output
12
Laviyx
Learn

${text:start:length} takes part of a string. The space before -5 is needed.

Replace text#

Terminal
path="a-b-c"
echo "${path/-/_}"
echo "${path//-/_}"
Output
a_b-c
a_b_c

One / replaces the first match, and two replace them all.

Trim from either end#

# removes from the start, and % from the end. Doubling them removes as much as possible:

FormRemoves
${v#pattern}The shortest match at the start
${v##pattern}The longest match at the start
${v%pattern}The shortest match at the end
${v%%pattern}The longest match at the end

These are the standard way to handle paths:

Terminal
file="/var/log/app.tar.gz"
echo "${file##*/}"
echo "${file%/*}"
echo "${file%.gz}"
name="${file##*/}"
echo "${name%%.*}"
echo "${name#*.}"
Output
app.tar.gz
/var/log
/var/log/app.tar
app
tar.gz

The first two give the same results as basename and dirname, without starting a new program.

Upper and lower case#

Terminal
word="Hello"
echo "${word^^}"
echo "${word,,}"
Output
HELLO
hello

Split a string#

Use IFS with read:

Terminal
line="asha:admin:hyderabad"
IFS=: read -r user role city <<< "$line"
echo "$user lives in $city"
IFS=, read -ra parts <<< "a,b,c"
echo "${#parts[@]} parts"
Output
asha lives in hyderabad
3 parts

<<< is a here string, which feeds one string to a command.

Common mistakes#

Practice#

Practice exercise

Given files=("photo.JPG" "notes.txt" "song.mp3"), print each file's extension in lower case.

Show solution
Terminal
files=("photo.JPG" "notes.txt" "song.mp3")
for f in "${files[@]}"; do
  ext="${f##*.}"
  echo "${ext,,}"
done
Output
jpg
txt
mp3

Key takeaway#

Next: Writing Safe Scripts.

Finished this lesson?