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:
apple
is at index 0banana
is at index 1cherry
is 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 valuesecho
printed 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
date
command - 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:
whoami
command 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_list
is better thanarr
orlist
- 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
tee
command - 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: