Arrays (Bash): verschil tussen versies

Uit De Vliegende Brigade
Naar navigatie springen Naar zoeken springen
(29 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 7: Regel 7:
 
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 only about these 'normal', 'straight' arrays. Not about associative arrays.
+
This article is about these 'normal', 'straight' arrays. Not about associative arrays.
  
 
== Base zero ==
 
== Base zero ==
Regel 43: Regel 43:
 
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.
 
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.
  
== Introduction ==
+
== 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 ==
 +
 
 +
<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>
 +
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>
 
<pre>
#!/bin/bash
+
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 55: Regel 122:
 
echo ${a[@]}  # Returns "1 2 3 4" - whole array
 
echo ${a[@]}  # Returns "1 2 3 4" - whole array
 
</pre>
 
</pre>
 
Nog een beginnetje:
 
  
 
<pre>
 
<pre>
Regel 62: Regel 127:
 
a[1]="Eén"
 
a[1]="Eén"
  
echo $a        # Toont alleen eerste element
+
echo $a        # Returns first element
echo $a[0]    # Output: Null[0] - Waarschijnlijk niet wat de bedoeling was
+
echo $a[0]    # Output: Null[0] - Probably not what you wanted?
 
echo ${a[1]}  # OK
 
echo ${a[1]}  # OK
 
</pre>
 
</pre>
Regel 72: Regel 137:
  
 
== Length of an array ==
 
== Length of an array ==
 
E.g.:
 
  
 
<pre>
 
<pre>
Regel 81: Regel 144:
 
== Return all indices ==
 
== Return all indices ==
  
<code>${!j[@]}</code> returns all indices. Can be handy as input for a loop. E.g.:
+
<pre>
 +
j=(I never get enough of creating nice arrays. And you?)
 +
echo ${!j[@]}
 +
</pre>
 +
 
 +
Output:
  
 
<pre>
 
<pre>
unset j
+
0 1 2 3 4 5 6 7 8 9
mapfile -t j < <( wp post list --post_type=product --field=ID --posts_per_page=64 )
+
</pre>
 +
 
 +
This might come handy for loops:
 +
 
 +
<pre>
 +
j=(I never get enough of creating nice arrays. And you?)
 +
echo ${!j[@]}
  
echo "Number of items: ${!j[@]}"
+
echo "Index Value"
 +
for i in ${!j[@]}
 +
do
 +
  echo "$i: ${j[$i]}"
 +
done
 
</pre>
 
</pre>
  
Output (some output removed):
+
Output:
  
 
<pre>
 
<pre>
Number of items: 0 1 2 3 ... 64 65
+
 
 +
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>
  
== Loop through array ==
+
== Loop through array entries ==
  
 
[https://www.cyberciti.biz/faq/bash-for-loop-array/ Dit] werkt. Merk op dat <code>$i</code> de waarde van de cel bevat, niet de index:
 
[https://www.cyberciti.biz/faq/bash-for-loop-array/ Dit] werkt. Merk op dat <code>$i</code> de waarde van de cel bevat, niet de index:
Regel 108: Regel 198:
 
</pre>
 
</pre>
  
== Loop through the index of an array ==
+
== 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'':
 
Rather than going through the ''entries'' of an array, let's loop through its ''index''. Note that this is ''base 0'':
Regel 127: Regel 217:
 
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 196: Regel 299:
 
== Merge arrays ==
 
== Merge arrays ==
  
Program:
 
 
<pre>
 
<pre>
 
i1=(1 2 drie vier)
 
i1=(1 2 drie vier)
Regel 217: Regel 319:
 
== Append ==
 
== Append ==
  
=== Single item using '+=' ===
+
''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 ===
  
 
<pre>
 
<pre>
Regel 231: Regel 335:
 
</pre>
 
</pre>
  
=== Another array using '+=' ===
+
=== Another array using '+=' shorthand operator ===
  
 
<pre>
 
<pre>
Regel 265: Regel 369:
 
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 296: Regel 516:
  
 
* [[Associative arrays (Bash)]]
 
* [[Associative arrays (Bash)]]
 +
* [[For-loops (Bash)]]
  
 
== Sources ==
 
== Sources ==
Regel 304: Regel 525:
 
* https://stackoverflow.com/questions/3112687/how-to-iterate-over-associative-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://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/

Versie van 7 mrt 2023 16:43

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 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