As a full-stack developer and Linux scripter, arrays play a vital role in enabling complex logic and data structures. Mastering bash arrays unlocks new possibilities for building robust automation.
This all-encompassing guide aims to make you an arrays expert by walking through every facet of working with arrays in bash.
What Are Arrays?
An array is a special variable that allows storing multiple elements under a single name with easy access and manipulation. The values can be numbers, strings, or even other variable names that evaluate to values.
Instead of creating distinct variables:
month1="January"
month2="February"
month3="March"
We can store the months in an array:
months=("January" "February" "March")
The array elements can be accessed by their index number starting at 0. So months[0] evaluates to "January", months[1] evaluates to "February" etc.
Arrays form the underlying data structure for more complex storage like stacks and queues in programming. They enable storing sequential collections of data for easy sorting, ordering and access.
Key Benefits of Using Arrays
Compared to individual variables, arrays offer important advantages:
Concise Storage: Store thousands of elements under a single variable name
Ordering: Elements indexed in specific order for sorting needs
Flexibility: Add, remove elements on the fly as needed
Iteration: Loop over elements without manual print statements
Abstraction: Hide complexity of data by encapsulating in array
These factors enable array usage for algorithm implementation, automated data pipelines, cleaner code and more.
Bash itself leverages arrays internally for storing positional parameters passed to scripts $@ as well as for filenames * glob expansion.
Array Types in Bash
The Bash scripting language supports two main types of arrays:
Indexed Arrays
These conventional arrays use integer indexes starting from 0. They can store any bash data type like numbers and strings.
Creation:
arr[0]=5
arr[1]=hello
Associative Arrays
These arrays use custom string indexes instead of integers. Elements are stored and accessed using key-value pairs.
Creation:
declare -A colors
colors["red"]="#FF0000"
colors["blue"]="#0000FF"
We will look at the specific syntax for indexed and associative arrays more in-depth later on.
Common Array Uses
Understanding how arrays are used in practice helps cement the conceptual foundation. Some typical use cases include:
Configuration Data
Store program configuration settings in arrays for easy access:
locations=("New York" "Los Angeles" "Chicago")
Command Line Arguments
Pass array contents as arguments to commands:
convert_images ${png_files[@]}
Temp Data
Use arrays as temporary data stores for transformation pipeline:
files=($(ls)) # store list of files
sort_files ${files[@]} # sort files
save_files ${files[@]} # save sorted files
Collections
Model real-world collections and data sets using arrays:
users=("john" "jane" "bob")
These examples demonstrate the flexibility of arrays for scripting tasks.
Declaration of Arrays
Unique array assignment syntax allows creating arrays to hold values:
1. Compound Array Assignment
Enclosing values in ()
automatically assigns them to indexes starting from 0:
fruits=("Apple" "Banana" "Orange")
We can verify array creation:
echo ${fruits[0]} # Apple
echo ${fruits[1]} # Banana
2. Manual Indexed Assignment
Elements can be appended to specific array indexes manually:
fruits[0]="Apple"
fruits[1]="Banana"
fruits[2]="Orange"
3. Read from File
Bash supports populating array directly from a file using IO redirection:
fruits=( $(cat fruits.txt) )
This reads the file content into an array where each line becomes an element.
4. String Split
Splitting a string on a delimiter into an array is possible as well:
vals="Apple,Banana,Orange"
fruits=(${vals//,/ })
The //
substitution operator splits vals
on comma into the array.
5. Array Function
Bash even provides an array function to create arrays from the output of other commands:
fruits=( $(array fruits Apple Banana Orange) )
These examples give a taste of the flexible syntax offered for array declaration.
Accessing Array Elements
Accessing populated array elements makes array utilities usable in scripts.
Indexed Access
Referring to indexed array elements directly prints the stored values:
fruits=("Apple" "Banana" "Orange")
echo ${fruits[0]} # Apple
echo ${fruits[1]} # Banana
Attempting to access an undefined index auto-expands the array with null
values:
echo ${fruits[3]} # null
Negative Index
Bash allows reverse array access using negative indexes starting from -1:
echo ${fruits[-1]} # Orange
echo ${fruits[-2]} # Banana
This simplifies referencing the last elements.
Slicing Expansion
Part of an array can be extracted using slicing expansion syntax:
echo ${fruits[@]:0:2} # Apple Banana
This prints the first 2 elements between indices 0 and 2.
For Loop
A common way to access each element is looping using bash for
:
for fruit in "${fruits[@]}"; do
echo $fruit
done
This loops through automatically printing each element.
Together these show some ways of directly accessing array contents.
Changing Array Elements
A key array feature is dynamically modifying stored values.
Assigning Indexes
Storing data to a particular index appends/overwrites value:
fruits[3]="Grape"
echo ${fruits[@]}
# Apple Banana Orange Grape
Here Grape
got appended by directly addressing index 3.
Appending Elements
Adding elements is also possible using the +=
operator:
fruits+=("Pineapple")
echo ${fruits[@]}
# Apple Banana Orange Grape Pineapple
Inserting Elements
Use slicing expansion to insert elements at specific positions:
fruits=("${fruits[@]:0:2}" "Watermelon" "${fruits[@]:2}")
This inserts "Watermelon" at index 2 by extracting 0-2 and then merging the parts. Elements at 2 get pushed forward.
Sorting Elements
Sort elements using sort
:
sorted=($(echo ${fruits[@]} | tr " " "\n" | sort))
echo ${sorted[@]}
# Apple Banana Grape Orange Pineapple Watermelon
Here tr
helped multi-line sort keeping elements intact.
These show some useful techniques for modifying populated arrays.
Removing Array Elements
Removing stored elements is equally important in data tasks.
Unset Elements
Use bash unset
to delete array elements by index:
unset fruits[0]
echo ${fruits[@]}
# Banana Orange Grape Pineapple Watermelon
For associative arrays, use the key:
unset colors["green"]
Restore Deleted Elements
Restoring deleted elements to their original indexes is possible too:
fruits=("${fruits[@]}" "Apple")
echo ${fruits[@]}
# Apple Banana Orange Grape Pineapple Watermelon
Here Apple
got merged into its original index.
Trim Excess Elements
Removing excess elements from arrays leaving no gaps simplifies them:
trimmed=(${fruits[@]})
echo ${trimmed[@]}
# Apple Banana Orange Grape Pineapple
Together these present some ways to delete elements or restore arrays.
Iterating Arrays with Loops
One of the prime reasons for using arrays is repeated access through loops for iteration.
For-in Loop
The basic for-in
loop allows addressing elements directly:
for fruit in "${fruits[@]}"; do
echo $fruit
done
C-style For Loop
Bash also offers C-style for loop syntax:
for((i=0; i<${#fruits[@]}; i++)); do
echo ${fruits[i]}
done
Here ${#fruits[@]}
tracks array size even if changed.
While Loop
A while loop variant is possible too:
i=0
while [[ $i -lt ${#fruits[@]} ]]; do
echo ${fruits[$i]}
((i++))
done
These give flexibility in programatically accessing arrays.
Associative Array Specifics
While indexed arrays use integer indexes, associative arrays use strings instead.
Creation
Here is an associative array storing hex color codes:
declare -A colors
colors["red"]="#FF0000"
colors["green"]="#00FF00"
Access Values
Access elements by custom string indexes:
echo ${colors["red"]} # FF0000
Bracket notation lookups the key.
List Keys
To output all keys:
echo ${!colors[@]}
# red green
Iteration
We can iterate the associative array:
for color in "${!colors[@]}"
do
echo $color = ${colors[$color]}
done
This shows the unique syntax for associative array handling.
Built-In Array Functions
Beyond language syntax, Bash offers functions to assist array manipulation:
Checking If Index Exists
Test if an array contains a specified element index:
colors=("red")
if [[ ${colors[0]+abc} ]]; then
echo "Index 0 exists"
else
echo "Index 0 does NOT exist"
fi
This checks array variable expansion to confirm index presence.
Expanding Into Arguments
Expand array elements into params for other commands with ${array[@]}
:
files=(1.txt 2.txt 3.txt)
wc ${files[@]}
# counts lines, words, bytes for each file
Passing the array expands it into arguments.
Getting Array Length
Find total elements stored using:
echo ${#colors[@]}
This prefixes #
to special length parameter.
Together these give a taste of useful array functions.
Multidimensional Arrays In Bash
Bash does not directly support multidimensional arrays. However, we can emulate 2D or 3D arrays using strings:
arr[0,0]=1
arr[0,1]=2
arr[1,0]=3
arr[1,1]=4
echo ${arr[1,0]} # 3
Here arr
works like a matrix using ,
to separate row/column indexes.
We need custom functions, but can model higher dimension data.
Arrays vs Other Languages
Developers coming from other languages may notice differences:
No Fixed Size
Bash arrays auto-grow and have no pre-set size limits for stored elements. Append freely.
No Type Safety
Arrays allow storing values of any data type. Mix strings, integers etc as needed.
No Native Methods
Methods like push, pop, shift etc need external functions unlike languages like JavaScript.
Limited Multidimension
Multi-dimension arrays require workarounds unlike Python/C#/Java native 2D/3D arrays.
Pass-By-Value
Bash passes arrays to functions by value unlike some languages pass by reference. Changes in functions do not affect caller array.
So adept bash array usage requires knowing these nuances.
Common Array Pitfalls
Like any programming concept, arrays come with some best practices:
Unset Elements Break Indexes
Deleting elements causes reindexing issues when later inserting expecting original order.
Pass By Value For Functions
Array changes inside functions do not apply to caller scope due to pass-by-value. Return arrays and assign back instead.
Iterate Defensively
Check array size before iterating in case elements got deleted or filter criteria impacts contents.
Declare Upfront
Declare arrays at start with declare -a
rather than sprinkling throughout code. Eases debugging by explicitness.
These examples serve to highlight array specific gotchas to guard against.
Unlocking Array Programming Potential
Bash arrays open up possibilities previously not feasible without significant effort:
Data Pipeline Automation
Ingest, process, transform and store data automatically using arrays as temporary buffers.
Report Generation
Extract elements into desired formats like CSV for plugging into reporting engines.
Input Parsing
Read stdin or files into array, iterate values performing validations, transformations and output.
Dynamic Configuration
Change system configuration by loading files into arrays and applying changes needed.
Algorithm Implementation
Craft custom data structures like stacks plus functions to deliver advanced programming techniques.
In essence, combining array data structures with Bash‘s innate automation strengths enables tackling more complex scripting challenges.
Key Takeaways
This comprehensive tutorial aimed to illustrate arrays extensively through key takeways like:
- Arrays enable storing collections of data under a single variable name
- Indexed and associative arrays provide integer vs string based element access
- Array slice syntax offers insertion, removal and ordering of elements
- Built-in functions help check existence, get size and expand elements
- Loops allow iterating arrays printing values or passing to commands
- Multidimensional data can be emulated using string indexes
- Arrays compared to other languages have pros but also pitfalls
- Practical automation uses benefit from array data pipelines
With this deep grounding in array concepts, Bash scripting to handle data sets and collections can reach new potentials.