Arrays and command substitution are two powerful features that will take your Bash scripting to the next level. Arrays let you store multiple values in a single variable, while command substitution allows you to capture and use command output dynamically. This guide explains everything in detail for absolute beginners.
🎯 What You'll Learn: In this hands-on tutorial, you'll discover:
- Creating and working with Bash arrays
- Understanding the mysterious
@symbol and array expansion - The difference between
"${array[@]}"and"$array" - Adding elements to arrays dynamically
- Command substitution with
$() - Capturing output from system commands
- Building dynamic scripts with real-time data
- Common pitfalls and how to avoid them
📦 Bash Arrays: Storing Multiple Values
Arrays are like containers that can hold multiple values under a single variable name. Instead of creating separate variables for each item, you can group related data together.
Why Use Arrays?
Without arrays (tedious):
fruit1="apple"
fruit2="banana"
fruit3="cherry"
With arrays (elegant):
fruits=("apple" "banana" "cherry")
Much cleaner and scalable!
🚀 Creating Your First Array
Let's create a script to explore arrays:
Step 1: Create the Script
nano array.sh
Step 2: Define a Simple Array
#!/bin/bash
fruits=("apple" "banana" "cherry")
echo "Fruits array: ${fruits[@]}"
Understanding the Array Syntax
Array Declaration
fruits=("apple" "banana" "cherry")
Purpose: Creates an array named fruits with three elements.
Syntax breakdown:
fruits=- Array variable name()- Parentheses indicate an array"apple" "banana" "cherry"- Array elements separated by spaces- Each element is quoted to handle spaces within values
💡 Array Indexing: Bash arrays are zero-indexed, meaning the first element is at position 0:
appleis at index 0bananais at index 1cherryis at index 2
Displaying the Entire Array
echo "Fruits array: ${fruits[@]}"
Purpose: Prints all elements of the array.
The @ symbol explained:
${fruits[@]}- Expands to all elements in the array- Each element is treated as a separate word
- The
@means "give me every item individually"
Step 3: Run the Script
bash array.sh
Output:
Fruits array: apple banana cherry
What happened:
- The array was created with three elements
${fruits[@]}expanded to all three valuesechoprinted them space-separated
🔍 Understanding Array Expansion: The @ Symbol Mystery
The @ symbol is crucial for array operations. Let's explore the differences between array access methods.
Experiment 1: The Wrong Way (Without @)
Update array.sh:
#!/bin/bash
fruits=("apple" "banana" "cherry")
for fruit in "$fruits"; do
echo "Fruit: $fruit"
done
Run it:
bash array.sh
Output:
Fruit: apple
What happened:
"$fruits"(without@) only accesses the first element (index 0)- The loop runs only once with "apple"
- The other elements are completely ignored!
⚠️ Common Mistake: Using $fruits or "$fruits" without [@] only gives you the first array element. Always use [@] to access all elements!
Experiment 2: The Right Way (With @)
Now update it correctly:
#!/bin/bash
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
Run it:
bash array.sh
Output:
Fruit: apple
Fruit: banana
Fruit: cherry
What happened:
"${fruits[@]}"expands to all elements individually- The loop iterates three times (once per element)
- Each fruit is processed separately
Understanding the Syntax Details
Let's break down "${fruits[@]}" piece by piece:
| Component | Purpose | Why Important |
|---|---|---|
"..." | Double quotes | Preserves spaces within elements |
${...} | Curly braces | Required for array syntax |
fruits | Array name | The variable holding your array |
[@] | Array subscript | Expands to all elements |
Experiment 3: What Happens Without Quotes?
Try this version:
#!/bin/bash
fruits=("apple" "banana" "cherry")
for fruit in ${fruits[@]}; do
echo "Fruit: $fruit"
done
This works for simple values, but watch what happens with spaces:
fruits=("apple pie" "banana" "cherry tart")
for fruit in ${fruits[@]}; do
echo "Fruit: $fruit"
done
Output:
Fruit: apple
Fruit: pie
Fruit: banana
Fruit: cherry
Fruit: tart
Problem: Without quotes, "apple pie" is split into two separate words!
With quotes "${fruits[@]}":
Fruit: apple pie
Fruit: banana
Fruit: cherry tart
Much better! Each element remains intact.
✅ Best Practice: Always use "${array[@]}" with quotes to properly handle array elements that contain spaces.
➕ Adding Elements to Arrays
Arrays are dynamic - you can add elements after creation.
The += Operator
Update your script:
#!/bin/bash
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
fruits+=("mango")
echo "Updated fruits array: ${fruits[@]}"
Run it:
bash array.sh
Output:
Fruit: apple
Fruit: banana
Fruit: cherry
Updated fruits array: apple banana cherry mango
Understanding the Addition
fruits+=("mango")
Purpose: Appends "mango" to the end of the array.
Syntax breakdown:
fruits+=- Append operator("mango")- Value(s) to add (in parentheses because it's an array operation)- The array now has 4 elements instead of 3
You can add multiple elements at once:
fruits+=("orange" "grape" "kiwi")
This adds three new elements in one operation.
📊 Array Operations Reference
| Operation | Syntax | Result |
|---|---|---|
| Create array | arr=("a" "b" "c") | New array with 3 elements |
| Access all elements | ${arr[@]} | All elements: a b c |
| Access one element | ${arr[0]} | First element: a |
| Array length | ${#arr[@]} | Number of elements: 3 |
| Append element | arr+=("d") | Array becomes: a b c d |
| Set element | arr[1]="z" | Array becomes: a z c |
| Get indices | ${!arr[@]} | All indices: 0 1 2 |
🔄 Command Substitution: Capturing Command Output
Command substitution lets you capture the output of a command and use it as a variable value. This is incredibly powerful for creating dynamic scripts.
The $() Syntax
The modern syntax for command substitution is $(command). Whatever the command prints becomes the variable's value.
Step 1: Create a Command Substitution Script
nano command_substitution.sh
Step 2: Capture System Information
CURRENT_DATE=$(date)
echo "Today is $CURRENT_DATE"
USER_NAME=$(whoami)
echo "Current user: $USER_NAME"
CURRENT_DIR=$(pwd)
echo "Current directory: $CURRENT_DIR"
Step 3: Run the Script
bash command_substitution.sh
Output:
Today is Fri Oct 3 11:57:43 PM PKT 2025
Current user: centos9
Current directory: /home/centos9/Razzaq-Labs-II/random/random
Understanding Each Command Substitution
Capturing the Date
CURRENT_DATE=$(date)
Purpose: Runs the date command and stores its output in CURRENT_DATE.
What happens:
- Bash sees
$(date) - Executes the
datecommand - Captures the output: "Fri Oct 3 11:57:43 PM PKT 2025"
- Assigns that string to
CURRENT_DATE
The date command: Displays the current system date and time.
Capturing the Username
USER_NAME=$(whoami)
Purpose: Captures the current user's username.
What happens:
whoamicommand runs- Outputs the username: "centos9"
- That value is stored in
USER_NAME
The whoami command: Prints the username of the current user.
Capturing the Current Directory
CURRENT_DIR=$(pwd)
Purpose: Stores the current working directory path.
What happens:
pwd(Print Working Directory) executes- Outputs the full path: "/home/centos9/Razzaq-Labs-II/random/random"
- Path is stored in
CURRENT_DIR
The pwd command: Shows the absolute path of your current location in the filesystem.
Displaying the Captured Values
echo "Today is $CURRENT_DATE"
echo "Current user: $USER_NAME"
echo "Current directory: $CURRENT_DIR"
Purpose: Displays the values we captured.
Key insight: These are now regular variables - the command only ran once during assignment. The variables hold the output as strings.
🎯 Practical Command Substitution Examples
Example 1: Counting Files
FILE_COUNT=$(ls -1 | wc -l)
echo "There are $FILE_COUNT files in this directory"
Breaking it down:
ls -1- Lists files, one per line|- Pipes the output to the next commandwc -l- Counts the lines- The final count is stored in
FILE_COUNT
Example 2: System Uptime
UPTIME=$(uptime -p)
echo "System has been running: $UPTIME"
The uptime -p command: Shows how long the system has been running in a human-readable format (e.g., "up 2 days, 5 hours").
Example 3: Disk Usage
DISK_USAGE=$(df -h / | tail -1 | awk '{print $5}')
echo "Root partition is $DISK_USAGE full"
Breaking it down:
df -h /- Shows disk usage for root partition in human-readable formattail -1- Gets the last line (the data line, not headers)awk '{print $5}'- Extracts the 5th column (percentage used)- Result might be: "17%"
Example 4: Building Dynamic Filenames
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_${TIMESTAMP}.tar.gz"
echo "Creating backup: $BACKUP_FILE"
Output example:
Creating backup: backup_20251003_235743.tar.gz
Why this is useful: Each backup gets a unique, sortable filename with the exact timestamp.
🔗 Combining Arrays and Command Substitution
You can use command substitution to populate arrays dynamically!
Example: Array of Running Processes
#!/bin/bash
# Capture all running bash processes into an array
PROCESSES=($(ps aux | grep bash | grep -v grep | awk '{print $2}'))
echo "Found ${#PROCESSES[@]} bash processes"
echo "Process IDs:"
for pid in "${PROCESSES[@]}"; do
echo " - PID: $pid"
done
What this does:
ps aux- Lists all processesgrep bash- Filters for bash processesgrep -v grep- Excludes the grep command itselfawk '{print $2}'- Extracts the process ID columnPROCESSES=(...)- Stores each PID as an array element${#PROCESSES[@]}- Counts how many PIDs found- Loop prints each PID
📊 Command Substitution Reference
| Command | Purpose | Example |
|---|---|---|
date | Current date and time | NOW=$(date) |
whoami | Current username | USER=$(whoami) |
pwd | Current directory | DIR=$(pwd) |
hostname | System hostname | HOST=$(hostname) |
uname -r | Kernel version | KERNEL=$(uname -r) |
wc -l | Count lines | LINES=$(wc -l < file.txt) |
🎯 Best Practices
✅ Array Best Practices
- Always quote array expansions: Use
"${array[@]}"to preserve elements with spaces - Use meaningful names:
user_listis better thanarrorlist - Initialize before use: Declare arrays before adding elements
- Check array length: Use
${#array[@]}before accessing elements - Use loops for processing: Iterate with
for item in "${array[@]}" - Document expected content: Comment what type of data the array holds
✅ Command Substitution Best Practices
- Use
$()not backticks: Modern syntax is clearer and nestable - Quote the results: Use
"$(command)"to preserve whitespace - Check command success: Verify the command worked before using output
- Capture stderr when needed: Use
$(command 2>&1)to include errors - Be aware of performance: Command substitution creates subshells
- Use in assignments: Always assign to variables for reusability
📝 Command Cheat Sheet
Array Operations
# Create array
my_array=("item1" "item2" "item3")
# Access all elements (correct way)
echo "${my_array[@]}"
# Access specific element (zero-indexed)
echo "${my_array[0]}" # First element
echo "${my_array[1]}" # Second element
# Get array length
echo "${#my_array[@]}"
# Add elements
my_array+=("item4")
my_array+=("item5" "item6")
# Loop through array
for item in "${my_array[@]}"; do
echo "$item"
done
# Get array indices
echo "${!my_array[@]}"
# Modify element
my_array[1]="new_value"
# Remove element (leaves gap)
unset my_array[1]
# Create array from command output
files=($(ls))
Command Substitution
# Basic syntax
result=$(command)
# With options
date_str=$(date +%Y-%m-%d)
# With pipes
line_count=$(cat file.txt | wc -l)
# Nested substitution
outer=$(echo "Inner: $(whoami)")
# Multiple commands
info=$(date; whoami; pwd)
# Assign to array
array=($(command))
# Use in string
message="User $(whoami) logged in at $(date)"
# Capture with error output
output=$(command 2>&1)
🚀 What's Next?
📚 Continue Learning
In Part 3, we'll cover:
- Output logging with
teecommand - Error handling and exit codes
- String manipulation techniques
- Finding string length
- Extracting substrings
- Replacing patterns in strings
- Practical file manipulation examples
Stay tuned for the next guide!
🎉 Congratulations! You've mastered Bash arrays and command substitution. You can now store multiple values efficiently and build dynamic scripts that capture real-time system information.
What did you think of this guide? Share your array and command substitution use cases in the comments below!
💬 Discussion
I'd love to hear about your experience:
- What types of data are you storing in arrays?
- Have you built any scripts using command substitution?
- Did you encounter the
@symbol confusion before? - What scripting challenges can I help with?
Connect with me:

