When handling multiple values in Bash, it is safer to keep them in an array than to pack them into a delimiter-separated string.
File names, command-line arguments, and service names in particular may contain spaces or characters that are interpreted as wildcards. By using arrays, you can add, reference, and iterate over each value while keeping it as an independent element.
Bash mainly uses the following two types of arrays.
- Indexed arrays: manage elements with numeric indexes
- Associative arrays: manage values with string keys
When iterating, it is important to enclose the entire array expansion in double quotes, as in "${array[@]}". This allows each element to be treated as a separate argument.
Step 1: Add elements to an indexed array
Section titled “Step 1: Add elements to an indexed array”Indexed arrays are used when you want to keep multiple values in order.
You can append elements with +=(), or assign a value by specifying an index. ${#items[@]} returns the number of elements, while ${!items[@]} returns the indexes currently in use.
bash -c 'items=("alpha" "two words")items+=("gamma")items[3]="delta"
printf "count=%s\n" "${#items[@]}"for index in "${!items[@]}"; do printf "%s=%s\n" "$index" "${items[$index]}"done'In this example, all four elements are preserved as independent values. two words also remains a single element and is not split into two because of the space.
Step 2: Manage keys and values with an associative array
Section titled “Step 2: Manage keys and values with an associative array”When you want to use strings as keys for values, declare an associative array with declare -A.
Assigning a value again to an existing key updates it, while assigning a value to a new key adds another element.
bash -c 'declare -A ports=( [http]=80 [ssh]=22)
ports[http]=8080ports[https]=443
printf "count=%s\n" "${#ports[@]}"printf "http=%s\n" "${ports[http]}"printf "https=%s\n" "${ports[https]}"printf "ssh=%s\n" "${ports[ssh]}"'In this example, the value of http is updated from 80 to 8080, and https is added as a new element.
Because values can be referenced by names that describe their purpose instead of numeric positions, associative arrays are suitable for managing settings and attributes.
Step 3: Safely iterate over elements that contain spaces
Section titled “Step 3: Safely iterate over elements that contain spaces”Use "${items[@]}" when iterating over the entire array.
The double-quoted form "${items[@]}" expands each array element as an independent value. This allows strings containing spaces or * to be processed as the contents of a single element.
bash -c 'items=("alpha" "two words" "*.conf")
printf "count=%s\n" "${#items[@]}"for item in "${items[@]}"; do printf "[%s]\n" "$item"done'two words is processed as one element in a single loop iteration, and *.conf is not expanded into a file list in this processing.
If you expand the array without quotes as shown below, word splitting caused by spaces and pathname expansion may affect the contents of the elements.
for item in ${items[@]}
When you want to preserve array element boundaries, use for item in "${items[@]}" as the basic form.
Step 4: Iterate over keys and values in an associative array
Section titled “Step 4: Iterate over keys and values in an associative array”You can obtain the list of keys in an associative array with ${!array[@]}.
By retrieving each key one at a time and referencing the array with that key, you can process key-value pairs.
bash -c 'declare -A labels=( [web]="http server" [db]="database service")
for key in "${!labels[@]}"; do printf "%s=%s\n" "$key" "${labels[$key]}"done'When iterating over an associative array, it is important not to make processing depend on the order in which keys appear. If a fixed order is required, sort the keys separately before processing them.
When retrieving a value, quote the expansion as in "${labels[$key]}". This ensures that a value containing spaces is still treated as a single value.
Step 5: Receive command-line arguments as an array
Section titled “Step 5: Receive command-line arguments as an array”When processing multiple arguments passed to a shell script, you can assign "$@" directly to an array.
With args=("$@"), the boundaries of each command-line argument are preserved when they are stored in the array.
bash -c 'args=("$@")args+=("four words")
printf "argc=%s\n" "${#args[@]}"for arg in "${args[@]}"; do printf "<%s>\n" "$arg"done' _ "alpha" "two words" "three"With bash -c, the _ immediately following the command string becomes $0, and the subsequent values alpha, two words, and three become positional parameters starting from $1.
In this example, the three received arguments are first stored in an array, and then four words is added. The two values containing spaces also remain individual elements.
There is no need to combine multiple values into a single string as shown below.
args="$*"
If multiple values need to be processed individually later, keeping them in an array from the start with args=("$@") allows you to preserve the element boundaries.
Key points for handling arrays safely
Section titled “Key points for handling arrays safely”When handling multiple values in Bash, the following basic forms help avoid unintended splitting.
- Add to an array:
items+=("value") - Expand all elements individually:
"${items[@]}" - Get the number of elements:
"${#items[@]}" - Get indexes or keys:
"${!items[@]}" - Store command-line arguments in an array:
args=("$@") - Create an associative array:
declare -A values
When multiple values are converted into a delimiter-separated string, additional processing is required later to restore the original element boundaries. When values can be passed as an array, it is better to keep each element as an independent value instead of concatenating them into a string.