Arrays (Bash): verschil tussen versies
(23 tussenliggende versies door dezelfde gebruiker niet weergegeven) | |||
Regel 1: | Regel 1: | ||
− | In Bash, an ''array'' is a one-dimensional sequence of variables. | + | In Bash, an ''array'' is a one-dimensional sequence of variables with an index. |
== An array has only one dimension == | == An array has only one dimension == | ||
Regel 5: | Regel 5: | ||
In Bash, there are no multi-dimensional arrays. You have ''[[Associative arrays (Bash) | associative arrays]]'' which may trick you in believing they are multi-dimensional, but they really aren't. | In Bash, there are no multi-dimensional arrays. You have ''[[Associative arrays (Bash) | 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 | + | * 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. | |
− | This article is | ||
== Base zero == | == Base zero == | ||
− | + | Bash arrays are ''base zero'': The index start at ''0'' and you can't change that: | |
<pre> | <pre> | ||
Regel 47: | Regel 46: | ||
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''. | 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 individual entries == |
+ | |||
+ | <pre> | ||
+ | unset j | ||
+ | j[0]="Foo" | ||
+ | j[1]="bar" | ||
+ | |||
+ | echo ${j[0]} # Foo | ||
+ | echo ${j[1]} # bar | ||
+ | echo ${j[@]} # Display all entries: Foo bar | ||
+ | echo ${#j[@]} # Number of entries: 2 | ||
+ | echo ${!j[@]} # Display ll indices: 0 1 | ||
+ | </pre> | ||
+ | |||
+ | == Assign an array using parenthesis == | ||
+ | |||
+ | OK, it has been demonstrated a couple of time by now, but just for the record: | ||
<pre> | <pre> | ||
− | #!/ | + | 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 | ||
+ | </pre> | ||
+ | |||
+ | Output: | ||
+ | |||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | |||
+ | Place entries that contain spaces, within double quotes: | ||
+ | |||
+ | <pre> | ||
+ | j=("This is the first entry" "This is the second entry") | ||
+ | echo ${#j[@]} | ||
+ | </pre> | ||
+ | |||
+ | Output: | ||
+ | |||
+ | <pre> | ||
+ | 2 | ||
+ | </pre> | ||
+ | |||
+ | == Index & syntax == | ||
+ | |||
+ | Just some starting stuff: | ||
+ | <pre> | ||
a=(1 2 "Drie" 4 "Vijf") | a=(1 2 "Drie" 4 "Vijf") | ||
Regel 59: | Regel 121: | ||
echo ${a[@]} # Returns "1 2 3 4" - whole array | echo ${a[@]} # Returns "1 2 3 4" - whole array | ||
</pre> | </pre> | ||
− | |||
− | |||
<pre> | <pre> | ||
Regel 66: | Regel 126: | ||
a[1]="Eén" | a[1]="Eén" | ||
− | echo $a # | + | echo $a # Returns first element |
− | echo $a[0] # Output: Null[0] - | + | echo $a[0] # Output: Null[0] - Probably not what you wanted? |
echo ${a[1]} # OK | echo ${a[1]} # OK | ||
</pre> | </pre> | ||
Regel 76: | Regel 136: | ||
== Length of an array == | == Length of an array == | ||
− | |||
− | |||
<pre> | <pre> | ||
Regel 85: | Regel 143: | ||
== Return all indices == | == Return all indices == | ||
− | < | + | <pre> |
+ | j=(I never get enough of creating nice arrays. And you?) | ||
+ | echo ${!j[@]} | ||
+ | </pre> | ||
+ | |||
+ | Output: | ||
<pre> | <pre> | ||
− | + | 0 1 2 3 4 5 6 7 8 9 | |
− | + | </pre> | |
+ | |||
+ | This might come handy for loops: | ||
+ | |||
+ | <pre> | ||
+ | j=(I never get enough of creating nice arrays. And you?) | ||
+ | echo ${!j[@]} | ||
− | echo " | + | echo "Index Value" |
+ | for i in ${!j[@]} | ||
+ | do | ||
+ | echo "$i: ${j[$i]}" | ||
+ | done | ||
</pre> | </pre> | ||
− | Output | + | Output: |
<pre> | <pre> | ||
− | + | ||
+ | 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? | ||
</pre> | </pre> | ||
Regel 131: | Regel 216: | ||
Index: 2 - Value: three | Index: 2 - Value: three | ||
</pre> | </pre> | ||
+ | |||
+ | == Loop through array index - This doesn't work == | ||
+ | |||
+ | <pre> | ||
+ | array_rows=12 | ||
+ | |||
+ | for i in $array_rows | ||
+ | do | ||
+ | echo $i | ||
+ | done | ||
+ | </pre> | ||
+ | |||
+ | The only output will be <code>12</code> - There won't be any loop. So, just giving a number as argument for a loop, doesn't work. | ||
== Unset == | == Unset == | ||
Regel 200: | Regel 298: | ||
== Merge arrays == | == Merge arrays == | ||
− | |||
<pre> | <pre> | ||
i1=(1 2 drie vier) | i1=(1 2 drie vier) | ||
Regel 220: | Regel 317: | ||
== Append == | == Append == | ||
+ | |||
+ | ''There is more than one way to skin a cat'', especially in Bash. Here are all the different ways that I know of, to append values: | ||
=== Single item using '+=' shorthand operator === | === Single item using '+=' shorthand operator === | ||
Regel 269: | Regel 368: | ||
6 | 6 | ||
1 2 3 4 enough Oops, just one more | 1 2 3 4 enough Oops, just one more | ||
+ | </pre> | ||
+ | |||
+ | === Single item using parenthesis === | ||
+ | |||
+ | <pre> | ||
+ | j=(1 2 3 4 enough) | ||
+ | j=(${j[@]} "No! Just one more, please!") | ||
+ | |||
+ | echo ${j[@]} | ||
+ | echo ${#j[@]} | ||
+ | </pre> | ||
+ | |||
+ | Output: | ||
+ | |||
+ | <pre> | ||
+ | 1 2 3 4 enough No! Just one more, please! | ||
+ | 6 | ||
+ | </pre> | ||
+ | |||
+ | === Multiple item using parenthesis === | ||
+ | |||
+ | The approach above, can easily be extended to add multiple items: | ||
+ | |||
+ | <pre> | ||
+ | j=(1 2 3 4 enough) | ||
+ | j=(${j[@]} 6 7 8 9 10) | ||
+ | </pre> | ||
+ | |||
+ | But this doesn't work: | ||
+ | |||
+ | <pre> | ||
+ | j=(1 2 3 4 enough) | ||
+ | j=(${j[@]} (6 7 8 9 10)) | ||
+ | </pre> | ||
+ | |||
+ | === Another array using parenthesis === | ||
+ | |||
+ | This is actually why I wrote this section on appending: | ||
+ | |||
+ | <pre> | ||
+ | j=(1 2 3 4 enough) | ||
+ | k=(blub blub 5 6 7 8 9 10) | ||
+ | j=(${j[@]} ${k[@]}) | ||
+ | |||
+ | echo ${j[@]} | ||
+ | echo ${#j[@]} | ||
+ | </pre> | ||
+ | |||
+ | Output: | ||
+ | |||
+ | <pre> | ||
+ | 1 2 3 4 enough blub blub 5 6 7 8 9 10 | ||
+ | 13 | ||
+ | </pre> | ||
+ | |||
+ | == Remove an entry == | ||
+ | |||
+ | <pre> | ||
+ | $ j=(one two three) | ||
+ | $ echo ${#j[@]) | ||
+ | 3 | ||
+ | |||
+ | $ echo ${!j[@]} | ||
+ | 0 1 2 | ||
+ | |||
+ | |||
+ | $ unset 'j[1]' | ||
+ | $ echo ${j[@]} | ||
+ | one three | ||
+ | |||
+ | $ echo ${#j[@]} | ||
+ | 2 | ||
+ | |||
+ | # Note that the index is not rebuild - An item is missing: | ||
+ | $ echo ${!j[@]} | ||
+ | 0 2 | ||
+ | </pre> | ||
+ | |||
+ | == Check for non-existence of an array == | ||
+ | |||
+ | Including for associative arrays: | ||
+ | |||
+ | <pre> | ||
+ | ################################################################################ | ||
+ | # Check for empty array | ||
+ | ################################################################################ | ||
+ | # | ||
+ | unset i | ||
+ | unset j | ||
+ | unset k | ||
+ | declare -gA j | ||
+ | |||
+ | j["foo"]=1 | ||
+ | j["bar"]=2 | ||
+ | |||
+ | k[1]=1 | ||
+ | k[2]=2 | ||
+ | |||
+ | if (( ${#i[@]} == 0 )); then | ||
+ | echo "i is empty" | ||
+ | else | ||
+ | echo "i is not empty" | ||
+ | fi | ||
+ | |||
+ | |||
+ | if (( ${#j[@]} == 0 )); then | ||
+ | echo "j is empty" | ||
+ | else | ||
+ | echo "j is not empty" | ||
+ | fi | ||
+ | |||
+ | if (( ${#k[@]} == 0 )); then | ||
+ | echo "k is empty" | ||
+ | else | ||
+ | echo "k is not empty" | ||
+ | fi | ||
</pre> | </pre> | ||
Regel 300: | Regel 515: | ||
* [[Associative arrays (Bash)]] | * [[Associative arrays (Bash)]] | ||
+ | * [[For-loops (Bash)]] | ||
== Sources == | == Sources == |
Huidige versie van 1 dec 2023 om 16:37
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
Bash arrays are base zero: The index start at 0 and you can't change that:
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 individual entries
unset j j[0]="Foo" j[1]="bar" echo ${j[0]} # Foo echo ${j[1]} # bar echo ${j[@]} # Display all entries: Foo bar echo ${#j[@]} # Number of entries: 2 echo ${!j[@]} # Display ll indices: 0 1
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
Loop through array index - This doesn't work
array_rows=12 for i in $array_rows do echo $i done
The only output will be 12
- There won't be any loop. So, just giving a number as argument for a loop, doesn't work.
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
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
There is more than one way to skin a cat, especially in Bash. Here are all the different ways that I know of, to append values:
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 parenthesis
j=(1 2 3 4 enough) j=(${j[@]} "No! Just one more, please!") echo ${j[@]} echo ${#j[@]}
Output:
1 2 3 4 enough No! Just one more, please! 6
Multiple item using parenthesis
The approach above, can easily be extended to add multiple items:
j=(1 2 3 4 enough) j=(${j[@]} 6 7 8 9 10)
But this doesn't work:
j=(1 2 3 4 enough) j=(${j[@]} (6 7 8 9 10))
Another array using parenthesis
This is actually why I wrote this section on appending:
j=(1 2 3 4 enough) k=(blub blub 5 6 7 8 9 10) j=(${j[@]} ${k[@]}) echo ${j[@]} echo ${#j[@]}
Output:
1 2 3 4 enough blub blub 5 6 7 8 9 10 13
Remove an entry
$ j=(one two three) $ echo ${#j[@]) 3 $ echo ${!j[@]} 0 1 2 $ unset 'j[1]' $ echo ${j[@]} one three $ echo ${#j[@]} 2 # Note that the index is not rebuild - An item is missing: $ echo ${!j[@]} 0 2
Check for non-existence of an array
Including for associative arrays:
################################################################################ # Check for empty array ################################################################################ # unset i unset j unset k declare -gA j j["foo"]=1 j["bar"]=2 k[1]=1 k[2]=2 if (( ${#i[@]} == 0 )); then echo "i is empty" else echo "i is not empty" fi if (( ${#j[@]} == 0 )); then echo "j is empty" else echo "j is not empty" fi if (( ${#k[@]} == 0 )); then echo "k is empty" else echo "k is not empty" fi
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/