Arrays (Bash)
In Bash, an array is a one-dimensional sequence of variables with an index.
An array has only one dimension
In Bash, there are no multi-dimensional arrays. You have associative arrays which may trick you in believing they are multi-dimensional, but they really aren't.
So, an array is pretty much like a vector: A sequence of entities.
This article is about these 'normal', 'straight' arrays. Not about associative arrays.
Base zero
With normal arrays, I don't think you get a say about how the index looks like. It's just an integer sequence, and it starts at 0: Bash arrays are base zero.
j=(1 2 3 blub Blub enough) echo "Array j: ${j[@]}" echo "Index j: ${!j[@]}"
Output:
Array j: 1 2 3 blub Blub enough Index j: 0 1 2 3 4 5
No declaration
An array doesn't need to be declared before being used:
j=(1 2 3 blub Blub enough) echo "Array j: ${j[@]}"
Output:
Array j: 1 2 3 blub Blub enough
Note that the entities are printed on one line - That might help you to detect variables that may look like arrays, but are just weird multi-line variables, or whatever.
It's called an entry
The thingy that an array is composed of, seems to be called entry. I'm surprised how many synonyms I know for this word, but when confused, I might just call it an entry.
Assign an array using parenthesis
OK, it has been demonstrated a couple of time by now, but just for the record:
j=(this is my shiny new array and it has 9 entries) echo "Size: ${#j[@]}" echo "Content: ${j[@]}" echo "Index Value" for i in ${!j[@]} do echo "$i: ${j[$i]}" done
Output:
Size: 11 Content: this is my shiny new array and it has 9 entries Index Value 0: this 1: is 2: my 3: shiny 4: new 5: array 6: and 7: it 8: has 9: 9 10: entries
Place entries that contain spaces, within double quotes:
j=("This is the first entry" "This is the second entry") echo ${#j[@]}
Output:
2
Index & syntax
Just some starting stuff:
a=(1 2 "Drie" 4 "Vijf") echo $a # Returns "1" - First item of the array echo $a[0] # Returns "1[0]" - The [0] part is treated as a literal echo ${a[0]} # Returns "1" echo ${a[@]} # Returns "1 2 3 4" - whole array
a[0]="Nul" a[1]="Eén" echo $a # Returns first element echo $a[0] # Output: Null[0] - Probably not what you wanted? echo ${a[1]} # OK
Associative arrays
An associative array is an array where the index doesn't have to be a number. I really like them, and I abuse them a lot to make you think they're actually multi-dimensional arrays. See Associative arrays (Bash) for more.
Length of an array
echo "Length of array j0: ${#j0[@]}"
Return all indices
j=(I never get enough of creating nice arrays. And you?) echo ${!j[@]}
Output:
0 1 2 3 4 5 6 7 8 9
This might come handy for loops:
j=(I never get enough of creating nice arrays. And you?) echo ${!j[@]} echo "Index Value" for i in ${!j[@]} do echo "$i: ${j[$i]}" done
Output:
0 1 2 3 4 5 6 7 8 9 Index Value 0: I 1: never 2: get 3: enough 4: of 5: creating 6: nice 7: arrays. 8: And 9: you?
Loop through array entries
Dit werkt. Merk op dat $i
de waarde van de cel bevat, niet de index:
array=( one two three ) for i in "${array[@]}" do echo "$i" done
Loop through array index
Rather than going through the entries of an array, let's loop through its index. Note that this is base 0:
array=( one two three ) for i in "${!array[@]}" do echo "Index: $i - Value: ${array[i]}" done
Output:
Index: 0 - Value: one Index: 1 - Value: two Index: 2 - Value: three
Unset
Use unset to destroy an array. But do you really need it?
Script:
j=(1 2 3 enough) echo "Size j: ${#j[@]}" j=(5 6) echo "Size j: ${#j[@]}" unset j echo "Size j: ${#j[@]}"
Output:
Size j: 4 Size j: 2 Size j: 0
- When you assign again a value to an array, the old values get overwritten - No need to use unset
- After unset, its length is 0
- Might be a good habit to unset an array after use, to free up the memory.
Reading output of a command into an array
I want to use WP-CLI command wp post list
to return IDs of products. I want to store these IDs in an array. Subsequently, I want to use parallelisation to do some nifty stuff with these IDs, without having to wait until tomorrow morning for the results.
This doesn't work:
j=$(wp post list --post_type=product --field=ID --posts_per_page=64)
Eventhough echo ${j[@]}
gives a promising result, I think it's just one string and not an array.
This works [1]:
unset j mapfile -t j < <( wp post list --post_type=product --field=ID --posts_per_page=64 | grep . ) echo "Whole array: ${j[@]}" echo "Item 5: ${j[5]}"
With grep .
to trim some leading whitespace.
BTW: mapfile
really creates an array, and not some kind of multi-line variable or a variable with a lot of spaces & numbers.
Arithmatic with arrays
This works, so no (())
black magic today. At least, not here:
i=5 echo "Entry i (=5): ${j[$i]}" echo "Entry i+1: ${j[$i+1]}" echo "Entry 6: ${j[6]}"
Merge arrays
Program:
i1=(1 2 drie vier) i2=(4 5 blub Blub) echo "Array i1: ${i1[@]}" echo "Array i2: ${i2[@]}" i3=( "${i1[@]}" "${i2[@]}" ) echo "Array i3: ${i3[@]}"
Output:
Array i1: 1 2 drie vier Array i2: 4 5 blub Blub Array i3: 1 2 drie vier 4 5 blub Blub
Append
Single item using '+=' shorthand operator
j=(1 2 3 4 enough) j+=("One more") echo ${j[@]}
Output:
1 2 3 4 enough One more
Another array using '+=' shorthand operator
j=(1 2 3 4 enough) k=(3 4 vijf) j+=("${k[@]}") echo ${j[@]}
Output:
1 2 3 4 enough 3 4 vijf
Single item by index
With ${#j[@]}
, we retrieve the number of entries. This would also be the index for a new entry, as Bash arrays are base zero:
j=(1 2 3 4 enough) echo ${#j[@]} j[${#j[@]}]="Oops, just one more" echo ${#j[@]} echo ${j[@]}
Output:
5 6 1 2 3 4 enough Oops, just one more
Single item using paranthesis
Case: Something doesn't add up (2022.10)
The output of a WP-CLI command is read into an array. But how long is the array? There seem to be 64, 65 and/or 66 items, depending on how you ask:
unset j mapfile -t j < <( wp post list --post_type=product --field=ID --posts_per_page=64 ) echo "Whole array: ${j[@]}" # 64 IDs echo "Item 5: ${j[5]}" echo "All indices: ${!j[@]}" # 0 to 65 = 66 indices echo "Number of items: ${#j[@]}" # 66
Answer: The first two entries contain NULL: WP-CLI creates quite some whitespace. Use grep .
to filter out this whitespace:
unset j mapfile -t j < <( wp post list --post_type=product --field=ID --posts_per_page=64 | grep . ) echo "Whole array: ${j[@]}" echo "Item 5: ${j[5]}" echo "All indices: ${!j[@]}" echo "Number of items: ${#j[@]}"
See also
Sources
- https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays
- https://linuxhint.com/associative_array_bash/
- https://stackoverflow.com/questions/11233825/multi-dimensional-arrays-in-bash
- https://stackoverflow.com/questions/3112687/how-to-iterate-over-associative-arrays-in-bash
- https://stackoverflow.com/questions/11426529/reading-output-of-a-command-into-an-array-in-bash
- https://linuxhint.com/bash_append_array/
- https://www.usingenglish.com/forum/threads/what-it-that-called-in-english.79248/