In Part 8 of the Linux Bash Course, we cover the structures required for writing maintainable and complex automation tools. You will learn how to declare and manipulate indexed and associative arrays, how to package code into reusable functions with custom return mechanisms, and how to use advanced parameter expansion for fast, built-in string parsing.
For more details on variable expansion modifiers, read the GNU Bash manual on Parameter Expansion.
Chapter 22: Arrays & Dictionaries
22.1 Indexed Arrays: Declaration
Indexed arrays store lists of values referenced by integer indices. They can be created implicitly by assignment or explicitly using the declare -a command.
- Syntax:
# Explicit declaration declare -a array_name # Inline initialization array_name=(value1 value2 value3) - Example Command:
declare -a TECH_STACK TECH_STACK=("Astro" "Docker" "Bash" "Azure") echo "Array declared successfully." - Expected Output:
Array declared successfully. - Flag & Command Breakdown:
declare -a: Instructs the shell to treat the specified variable name as an indexed array.
22.2 Assigning and Accessing Array Elements
Array indices start at 0. When accessing array elements, you must wrap the variable in curly braces (e.g., ${array[index]}) to prevent the shell from interpreting the index brackets as normal string characters.
- Syntax:
array[index]=value ${array[index]} - Example Command:
COURSES=("Linux" "Git" "Python") COURSES[1]="Git-VCS" # Reassign index 1 echo "First element: ${COURSES[0]}" echo "Second element: ${COURSES[1]}" - Expected Output:
First element: Linux Second element: Git-VCS
22.3 Getting All Elements: ${arr[@]}
To expand all elements in an array, use the index @ or *.
-
"${arr[@]}": Expands each element as a distinct, quoted string. This is the safest choice for arrays containing values with spaces. -
"${arr[*]}": Merges all array elements into a single string separated by the first character of theIFSvariable. -
Syntax:
"${array[@]}" "${array[*]}" -
Example Command:
SERVERS=("web-01 server" "database-01 server") echo "Using \${SERVERS[*]}:" for s in "${SERVERS[*]}"; do echo "Server: $s" done echo "Using \${SERVERS[@]}:" for s in "${SERVERS[@]}"; do echo "Server: $s" done -
Expected Output:
Using ${SERVERS[*]}: Server: web-01 server database-01 server Using ${SERVERS[@]}: Server: web-01 server Server: database-01 server
22.4 Getting Array Length: ${#arr[@]}
Prepending a # character inside the parameter expansion brackets returns the number of elements in the array.
- Syntax:
${#array_name[@]} - Example Command:
DEVICES=("PC" "Phone" "Tablet" "Router") echo "Total devices: ${#DEVICES[@]}" - Expected Output:
Total devices: 4
22.5 Slicing Arrays
You can extract a subset of array elements using the slicing syntax, which takes a start index and an optional length parameter.
- Syntax:
"${array[@]:start:length}" - Example Command:
NUMBERS=(zero one two three four five) echo "Slice (index 2, length 3): ${NUMBERS[@]:2:3}" echo "Slice (from index 3 onward): ${NUMBERS[@]:3}" - Expected Output:
Slice (index 2, length 3): two three four Slice (from index 3 onward): three four five
22.6 Associative Arrays (Dictionaries)
Associative arrays map values to string keys (key-value dictionaries) instead of integer indices.
-
Important: You must explicitly declare associative arrays using the
declare -Acommand before using them. -
Syntax:
declare -A dictionary_name dictionary_name[key]=value -
Example Command:
declare -A SITE_METRICS SITE_METRICS[domain]="freetechlearner.com" SITE_METRICS[uptime]="99.9%" SITE_METRICS[ssl]="active" echo "Domain: ${SITE_METRICS[domain]}" echo "SSL Status: ${SITE_METRICS[ssl]}" -
Expected Output:
Domain: freetechlearner.com SSL Status: active -
Flag & Command Breakdown:
declare -A: Declares an associative (dictionary) array.
22.7 Iterating Over Arrays
To iterate over array keys instead of values, prepend an exclamation mark (!) to the array name.
- Syntax:
# Loop over keys for key in "${!array[@]}"; do ... - Example Command:
declare -A USER_ROLES USER_ROLES[suresh]="admin" USER_ROLES[john]="developer" for user in "${!USER_ROLES[@]}"; do echo "User: $user -> Role: ${USER_ROLES[$user]}" done - Expected Output:
User: suresh -> Role: admin User: john -> Role: developer
22.8 Removing Array Elements
Use the unset command to delete a specific element at an index/key or to delete an entire array.
- Syntax:
unset array[index] # Removes single element unset array # Removes entire array - Example Command:
FRUITS=("Apple" "Banana" "Orange") unset FRUITS[1] # Delete Banana echo "Fruits array length: ${#FRUITS[@]}" echo "Elements matching keys: ${FRUITS[@]}" - Expected Output:
Fruits array length: 2 Elements matching keys: Apple Orange
22.9 Array from Command Output
You can use mapfile (also called readarray) to load lines of command output or files directly into an array. This is safer than parsing raw string loops.
- Syntax:
mapfile -t array_name < <(command) - Example Command:
printf "Line_1\nLine_2\nLine_3\n" > data.txt declare -a FILE_LINES mapfile -t FILE_LINES < data.txt echo "First Line: ${FILE_LINES[0]}" echo "Second Line: ${FILE_LINES[1]}" - Expected Output:
First Line: Line_1 Second Line: Line_2 - Flag & Command Breakdown:
mapfile: Reads lines from standard input into an indexed array.-t: Strips the trailing newline character from each line.
22.10 Passing Arrays to Functions
Bash functions do not support receiving array variables directly. You must expand the array into a list of parameters when calling the function, and then reconstruct it inside the function body.
- Example Script:
#!/bin/bash show_list() { local -a func_arr=("$@") # Reconstruct array echo "Items received: ${#func_arr[@]}" for item in "${func_arr[@]}"; do echo " - $item" done } SYSTEMS=("Linux" "Windows" "macOS") show_list "${SYSTEMS[@]}"
Chapter 23: Functions in Bash
23.1 Defining a Function (Two Syntaxes)
Functions group reusable blocks of code. You can define them using either of two equivalent syntaxes:
- Syntax:
# Method 1 (Standard) function_name() { # commands } # Method 2 (Word Prefix) function function_name { # commands }
23.2 Calling a Function
To invoke a function, call its name like any normal shell command. Do not use parentheses () when calling a function.
- Syntax:
function_name - Example Command:
greet_user() { echo "Welcome back, developer!" } greet_user - Expected Output:
Welcome back, developer!
23.3 Passing Arguments to Functions
Functions capture arguments using local positional parameters ($1, $2, etc.), which are independent of the global script arguments.
- Syntax:
function_name arg1 arg2 - Example Command:
setup_env() { echo "Environment: $1" echo "Version: $2" } setup_env "Production" "v1.2.0" - Expected Output:
Environment: Production Version: v1.2.0
23.4 Returning Values (Numeric Only)
The return statement in Bash returns an exit status (an integer from 0 to 255) to the caller, not a string or complex value.
-
Convention:
0represents success, and non-zero values indicate errors. -
Syntax:
return exit_code -
Example Command:
check_root() { if [[ $EUID -eq 0 ]]; then return 0 # Success else return 1 # Error fi } check_root echo "Is root exit code: $?" -
Expected Output:
Is root exit code: 1 -
Flag & Command Breakdown:
$EUID: A read-only shell variable holding the effective User ID of the current user. Root users always have a UID of0.
23.5 Capturing Function Output
To capture text output from a function, capture stdout using command substitution $().
- Syntax:
variable=$(function_name) - Example Command:
get_server_time() { date +"%H:%M" } CURRENT_TIME=$(get_server_time) echo "Server time: $CURRENT_TIME" - Expected Output:
Server time: 13:40
23.6 Local Variables in Functions
Always declare internal function variables with the local keyword. This prevents functions from accidentally overwriting variables defined in the global script scope.
- Syntax:
local variable_name=value
23.7 Function Libraries (Sourcing Files)
You can build libraries of reusable functions by putting them in helper files (e.g. lib.sh) and loading them using the source command.
- Syntax:
source /path/to/library.sh - Example Command:
# Create library echo "sys_log() { echo \"[LOG] \$1\"; }" > lib.sh # Source library source lib.sh sys_log "Libraries imported successfully." rm -f lib.sh - Expected Output:
[LOG] Libraries imported successfully.
23.8 Recursive Functions
Bash supports recursive functions—functions that call themselves.
-
Important: You must define clear exit conditions to prevent infinite loops, which will exhaust shell memory and cause script crashes.
-
Example Script:
#!/bin/bash # Calculate factorial recursively factorial() { local num=$1 if [[ $num -le 1 ]]; then echo 1 else local prev=$((num - 1)) local sub_factorial=$(factorial $prev) echo $((num * sub_factorial)) fi } echo "Factorial of 5 is: $(factorial 5)"
23.9 Exporting Functions
You can make functions available to subshells and child processes using the -f flag with the export command.
- Syntax:
export -f function_name - Example Command:
say_hello() { echo "Hello from subshell" } export -f say_hello bash -c "say_hello" - Expected Output:
Hello from subshell - Flag & Command Breakdown:
export -f: Exports the specified function name to the environment of subsequent commands.bash -c: Evaluates the following string command block in a new subshell environment.
23.10 Function Overriding and Hints
You can override standard system commands by defining a function with the same name. To run the original system command from inside an overridden function, prefix the command with the command keyword.
- Example Command:
# Override standard ls command ls() { echo "Listing directory securely..." command ls -lh "$@" } ls test_run.sh - Expected Output:
Listing directory securely... -rwxr-xr-x 1 suresh suresh 17 Jun 12 13:35 test_run.sh
Chapter 24: String Manipulation
24.1 String Length: ${#string}
Returns the total character count of a string.
- Syntax:
${#string_variable} - Example Command:
TEXT="BashScript" echo "Length: ${#TEXT}" - Expected Output:
Length: 10
24.2 Substring Extraction: ${string:start:len}
Extracts a range of characters from a string, starting at a 0-based offset. If length is omitted, it extracts to the end of the string.
- Syntax:
${variable:offset:length} - Example Command:
URL="freetechlearner.com" echo "Protocol-like prefix: ${URL:0:4}" echo "Suffix: ${URL:12}" - Expected Output:
Protocol-like prefix: free Suffix: er.com
24.3 Removing Shortest Prefix: ${string#pattern}
Trims the shortest matching pattern from the beginning of a string.
- Syntax:
${variable#pattern} - Example Command:
PATH_VAL="/usr/local/bin" echo "Trimmed prefix: ${PATH_VAL#*/}" - Expected Output:
Trimmed prefix: usr/local/bin
24.4 Removing Longest Prefix: ${string##pattern}
Trims the longest matching pattern (greedy match) from the beginning of a string. This is commonly used to extract filenames from absolute file paths.
- Syntax:
${variable##pattern} - Example Command:
FILE_PATH="/var/log/nginx/access.log" echo "Filename only: ${FILE_PATH##*/}" - Expected Output:
Filename only: access.log
24.5 Removing Shortest Suffix: ${string%pattern}
Trims the shortest matching pattern from the end of a string. This is useful for stripping file extensions.
- Syntax:
${variable%pattern} - Example Command:
FILE="main.go.bak" echo "Trimmed extension: ${FILE%.*}" - Expected Output:
Trimmed extension: main.go
24.6 Removing Longest Suffix: ${string%%pattern}
Trims the longest matching pattern (greedy match) from the end of a string.
- Syntax:
${variable%%pattern} - Example Command:
FILE="main.go.bak" echo "Base name: ${FILE%%.*}" - Expected Output:
Base name: main
24.7 Search and Replace: ${string/old/new}
Replaces the first occurrence of a matching pattern in a string with a new value.
- Syntax:
${variable/pattern/replacement} - Example Command:
ANIMAL="black cat, white cat" echo "Replaced: ${ANIMAL/cat/dog}" - Expected Output:
Replaced: black dog, white cat
24.8 Global Replace: ${string//old/new}
Replaces all occurrences of a matching pattern in a string.
- Syntax:
${variable//pattern/replacement} - Example Command:
ANIMAL="black cat, white cat" echo "Global replaced: ${ANIMAL//cat/dog}" - Expected Output:
Global replaced: black dog, white dog
24.9 Uppercase/Lowercase: ${string^} and ${string,,}
-
${var^}: Capitalizes the first character. -
${var^^}: Converts the entire string to uppercase. -
${var,}: Lowercases the first character. -
${var,,}: Converts the entire string to lowercase. -
Example Command:
STR="linuxBASH" echo "Upper: ${STR^^}" echo "Lower: ${STR,,}" echo "Capitalize First: ${STR^}" -
Expected Output:
Upper: LINUXBASH Lower: linuxbash Capitalize First: LinuxBASH
24.10 Parameter Expansion Default Values
You can use parameter expansion to handle unset or empty variables gracefully:
-
${var:-default}: Returnsdefaultif variable is unset or empty, but does not modify the variable. -
${var:=default}: Returnsdefaultif variable is unset or empty, and assigns it to the variable. -
${var:?error_msg}: Printserror_msgand exits the script if the variable is unset or empty. -
Example Command:
unset DB_PORT echo "Port default: ${DB_PORT:-5432}" echo "Original variable state: $DB_PORT" echo "Port assign: ${DB_PORT:=5432}" echo "Variable state after assignment: $DB_PORT" -
Expected Output:
Port default: 5432 Original variable state: Port assign: 5432 Variable state after assignment: 5432