bash: arraysArrays: lists and key-value maps in bash
An indexed array holds a list (hosts, files, command arguments) and an associative array maps keys to values (service to port). Arrays are the safe way to keep items that contain spaces, and to build a command's argument list piece by piece. Expand them as "${arr[@]}" and they stay intact.
a=(x y z) a+=(w) "${a[@]}" ${a[0]} ${#a[@]} ${!a[@]} declare -A m=([k]=v) mapfile -t a < fileKeep a list of hosts (one name has a space in it) and loop over it correctly.
for h inLoop over the words produced by the expansion that follows.
Bash arrays live only in the shell's memory: they are not strings, and they can't be exported to child processes. An indexed array is sparse: indexes are integers, need not be contiguous, and unset a[1] leaves a hole (${!a[@]} lists the indexes that exist). An associative array, created only with declare -A, is a hash table from string keys to string values and keeps no particular order. The key detail is expansion: "${a[@]}" produces one word per element with no word splitting or globbing, which is why arrays are the correct way to hold file names and argument lists. "${a[*]}" joins all elements into one word with the first character of IFS, and unquoted ${a[@]} is split again on spaces. Inside [ ] of an indexed array the subscript is evaluated as arithmetic, which is why a word like orders-api silently becomes index 0 if you forget declare -A. mapfile (readarray) reads a file or stream into an array, one element per line.
a=(x "y z" w)Create an indexed array with three elementsa+=(v)Append one or more elements"${a[@]}"Every element, each as its own word: the form to use 95% of the time${a[0]} ${a[-1]}First element / last element (bash 4.3+)${#a[@]}Number of elements${!a[@]}List of indexes (or keys for an associative array)"${a[@]:1:2}"Slice: 2 elements starting at index 1"${a[*]}"All elements joined into one string with the first char of IFSunset 'a[2]'Remove one element (the index stays as a gap)declare -A mCreate an associative array (required before string keys)[[ -v m[key] ]]True if the key exists (bash 4.3+)mapfile -t a < fileRead lines into an array; -t strips the newlinesdeclare -p aPrint the array with its indexes: the best debug viewfor h in ${hosts[@]} or for h in ${hosts[*]}Unquoted, elements are split on spaces and globbed again, so 'db primary' becomes two items.
for h in "${hosts[@]}"echo $hosts to print the array$hosts on its own is just ${hosts[0]}, the first element.
declare -p hosts # or: printf '%s\n' "${hosts[@]}"port=([orders-api]=8080) without declare -AIt becomes an indexed array; string subscripts are evaluated as arithmetic and silently collapse to index 0.
declare -A port=([orders-api]=8080)files=($(ls *.log)) or hosts=($(cat hosts.txt))Command output is word-split and glob-expanded: names with spaces break and a * in the data expands to file names.
files=(*.log) mapfile -t hosts < hosts.txtexport hosts and expecting a child script to see the arrayThe environment can only carry strings. Arrays can't be exported.
Pass the elements as arguments: ./child.sh "${hosts[@]}"Try these on a Linux machine of your own. Each task has a hint and a solution.
Create an array with the names web-01, web-02 and 'db primary', then print how many elements it has.
Read ~/data/hosts.txt into an array with mapfile and print the last host.
Make an associative array with orders-api=8080 and payments=8081 and print every key with its value.
Count how many requests each status code has in ~/logs/access.log using an associative array (status is field 9).
bash: arraysArrays: lists and key-value maps in bash
An indexed array holds a list (hosts, files, command arguments) and an associative array maps keys to values (service to port). Arrays are the safe way to keep items that contain spaces, and to build a command's argument list piece by piece. Expand them as "${arr[@]}" and they stay intact.
a=(x y z) a+=(w) "${a[@]}" ${a[0]} ${#a[@]} ${!a[@]} declare -A m=([k]=v) mapfile -t a < fileKeep a list of hosts (one name has a space in it) and loop over it correctly.
for h inLoop over the words produced by the expansion that follows.
Bash arrays live only in the shell's memory: they are not strings, and they can't be exported to child processes. An indexed array is sparse: indexes are integers, need not be contiguous, and unset a[1] leaves a hole (${!a[@]} lists the indexes that exist). An associative array, created only with declare -A, is a hash table from string keys to string values and keeps no particular order. The key detail is expansion: "${a[@]}" produces one word per element with no word splitting or globbing, which is why arrays are the correct way to hold file names and argument lists. "${a[*]}" joins all elements into one word with the first character of IFS, and unquoted ${a[@]} is split again on spaces. Inside [ ] of an indexed array the subscript is evaluated as arithmetic, which is why a word like orders-api silently becomes index 0 if you forget declare -A. mapfile (readarray) reads a file or stream into an array, one element per line.
a=(x "y z" w)Create an indexed array with three elementsa+=(v)Append one or more elements"${a[@]}"Every element, each as its own word: the form to use 95% of the time${a[0]} ${a[-1]}First element / last element (bash 4.3+)${#a[@]}Number of elements${!a[@]}List of indexes (or keys for an associative array)"${a[@]:1:2}"Slice: 2 elements starting at index 1"${a[*]}"All elements joined into one string with the first char of IFSunset 'a[2]'Remove one element (the index stays as a gap)declare -A mCreate an associative array (required before string keys)[[ -v m[key] ]]True if the key exists (bash 4.3+)mapfile -t a < fileRead lines into an array; -t strips the newlinesdeclare -p aPrint the array with its indexes: the best debug viewfor h in ${hosts[@]} or for h in ${hosts[*]}Unquoted, elements are split on spaces and globbed again, so 'db primary' becomes two items.
for h in "${hosts[@]}"echo $hosts to print the array$hosts on its own is just ${hosts[0]}, the first element.
declare -p hosts # or: printf '%s\n' "${hosts[@]}"port=([orders-api]=8080) without declare -AIt becomes an indexed array; string subscripts are evaluated as arithmetic and silently collapse to index 0.
declare -A port=([orders-api]=8080)files=($(ls *.log)) or hosts=($(cat hosts.txt))Command output is word-split and glob-expanded: names with spaces break and a * in the data expands to file names.
files=(*.log) mapfile -t hosts < hosts.txtexport hosts and expecting a child script to see the arrayThe environment can only carry strings. Arrays can't be exported.
Pass the elements as arguments: ./child.sh "${hosts[@]}"Try these on a Linux machine of your own. Each task has a hint and a solution.
Create an array with the names web-01, web-02 and 'db primary', then print how many elements it has.
Read ~/data/hosts.txt into an array with mapfile and print the last host.
Make an associative array with orders-api=8080 and payments=8081 and print every key with its value.
Count how many requests each status code has in ~/logs/access.log using an associative array (status is field 9).