Managing and Using Arrays in Shell Scripts (page 3)

botond published 2019. 03. 11., h - 17:10 time

3. page content

 

Continuation

Az previous page we got to know the use of indexed arrays in branches and functions, here we continue to use arrays to get acquainted with associative arrays.

 

 

Associative arrays

Associative arrays are very similar to indexed arrays, with the difference that in addition to number indexes, you can even add any text index (also known as keys) to identify the elements of the array. They are used in much the same way as described for indexed arrays, but there are some differences. Thus, in this chapter, we also highlight these differences.

Using Associative Arrays a Bash It was included in 4.0, so it was not available in the older Bash version.

Declaration, assignment

Declaring associative arrays differs from indexed arrays in that declarations are required here:

declare -A asszociativ_tomb

Indexed arrays are small -a option to declare (optional), and here is the big one -A with a must. Take care of this!

First, try to load a value into an associative array without a declaration to see what happens if we forget the declaration:

proba["elem1"]="Első elem"

Did not make a mistake. Now let's query it in the way we already know:

echo ${proba["elem1"]}

And it returns the "First Element" value. Hurray!

But if you query it in any of the following ways:

echo ${proba["elem2"]}
echo ${proba["masik_elem"]}
echo ${proba[0]}
echo $proba

In each case, we return the "First Element" value you specified at the beginning. What's going on here?

The answer is simple. We tried to create the associative array without a declaration, then Bash created a plain indexed array instead. And because the field name specified in square brackets was evaluated as zero, the specified value was placed in element 0 of the indexed array. Then, in either way, we queried our array that we thought was associative, returning the same result for each, since in each case the value specified in place of the index was evaluated to 0. A simple ($ proba) reading of the variable returns the value of the very first, that is, element 0, in the case of indexed arrays. (Accordingly, for example, echo $ {proba [1]} command will return a blank.)

So now we have an indexed array that, if we try to re-declare it as associative:

declare -A proba

Then we get the following error message:

bash: declare: proba: nem lehetséges az indexelt tömb asszociatívvá alakítása

Therefore, we first delete the array and then declare it again as associative:

unset proba
declare -A proba

Then our array is created.

Now you can add values ​​in many ways:

proba[elem1]="Első elem"
proba["elem2"]="Második elem"
proba[kecske]=káposzta

Then, in a single-line loop, you can read it just like you would for an indexed array:

for i in "${!proba[@]}"; do echo "$i: ${proba[$i]}" ; done
kecske: káposzta
elem3: Harmadik elem
elem2: Második elem
elem1: Első elem

Alternatively, if you want to populate it immediately with the declaration and in clear form, you can do so in a Shell script, for example:

1
2
3
4
5
6
7
8
#!/bin/bash
 
declare -A proba=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
    [kecske]="káposzta"
)
If you do not use quotation marks for a multi-word value when loading the associative array index-> ​​value pairs, you receive the following error message: "... when assigning to an associative array, the index must be specified".
Unlike plain indexed arrays, where you take the separate word as a separate value, it waits for the indexes and values ​​alternately in parentheses. So if a multiple word value is not enclosed in quotation marks, it assumes that an index has been omitted, so it returns an error.
Therefore, to avoid errors, you should get used to using quotation marks when specifying any value type.

Using multi-word keys

If you can already enter text indexes / keys, you will also be required to enter multi-word keys. Let's see how this happens:

1
2
3
4
5
6
7
#!/bin/bash
declare -A tomb=(
    [több szavas kulcs 1]="Idézőjel nélkül adtuk meg a kulcsot"
    ["több szavas kulcs 2"]="Itt pedig idézőjellel"
)
 
declare -A

In the end the declare -A command tells us what the array looks like in memory:

declare -A tomb=(["több szavas kulcs 1"]="Idézőjel nélkül adtuk meg a kulcsot" ["több szavas kulcs 2"]="Itt pedig idézőjellel" )

Here we can see that both assignments were successful, whether we entered the keys with or without quotation marks, the system replaced them where they were not specified. Due to the presence of square brackets, you can also interpret multi-word labels here. However, it is a good idea to use quotation marks if you are already using multi-word names in array indexes to stay standard.

Read values

You can read values ​​from associative arrays in the same way as for indexed arrays: You can cycle through the indices or refer directly to a given element. Essentially, the loop uses a direct reference to read, only then does the loop provide the array keys.

In the following example program, we populate an associative array and look up its elements with the given pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
    [kecske]="káposzta"
    [több szavas kulcs 1]="Idézőjel nélkül adtuk meg a kulcsot"
    ["több szavas kulcs 2"]="Itt pedig idézőjellel"
)
 
# Keresendő karakterlánc
keres="elem"
 
for i in "${!tomb[@]}"; do
    if [[ ${tomb[$i]} == *"$keres"* ]]; then
        echo "Találat: [$i]: ${tomb[$i]}"
    fi
done

Read array properties

Array properties work the same as for indexed arrays:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
)
 
echo "Összes elem kiolvasása egyszerre"
echo ${tomb[*]}         # Egy mezőként bontja ki
echo ${tomb[@]}         # Külön mezőkbe bontja ki 
# (ugyanaz a működés, mint az indexelt tömböknél)
echo
echo"Aposztrófokba burkolja az elemeket."
echo "Egy mezőként bontja ki:"
printf "'%s' \n" "${tomb[*]}"
echo "Több mezőként bontja ki:"
printf "'%s' \n" "${tomb[@]}"
echo
echo "Tartomány kiolvasása"
echo ${tomb[@]:2:2}
echo
echo "Összes index kiolvasása:"
echo ${!tomb[@]}
echo
echo "Tömb elemeinek a száma:"
echo ${#tomb[@]}
echo
echo "Megadott elem karakterhosszának a kiolvasása:"
echo ${#tomb["elem1"]}

Stitching Arrays

The stitching process is the same:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
)
 
tomb+=(
    [elem4]="negyedik elem"
    [elem5]="ötödik elem"
)
 
echo ${tomb[@]}

But with the difference that the stitching method familiar to indexed arrays doesn't work here:

tomb=("${tomb[@]}" "2000" "következő elem")

In this case, make the following error: "tomb: '$ {tomb [@]}': must be specified when assigning to an associative array"

So only the above method can be appended, that is, if the indices are given as well.

 

 

Using Arrays in Cycles

The difference here is that you can create multiple word keys for associative arrays. This way, if the list of indexes is not expanded correctly in a cycle, it does not give an error here, but returns an empty value, because although the addressing is syntactically correct, it does not find any value on the non-existent index. Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
)
 
echo "Tömb indexek ciklusban történő helyes kibontása:"
for i in "${!tomb[@]}"; do
    echo "Index: $i, értéke: ${tomb[$i]}"
done
 
echo
echo "Helytelen használat, nincs visszatérő érték:"
for i in "${!tomb[*]}"; do
    echo "Index: $i, értéke: ${tomb[$i]}"
done

output:

Tömb indexek ciklusban történő helyes kibontása:
Index: elem3, értéke: Harmadik elem
Index: elem2, értéke: Második elem
Index: elem1, értéke: Első elem

Helytelen használat, nincs visszatérő érték:
Index: elem3 elem2 elem1, értéke:

As shown in the example, extractions with @ and * produce exactly the same different behavior as with indexed arrays, except here for associative arrays, this does not cause an error.

Using arrays in junctions

Associative arrays can be used in junctions in exactly the same way as indexed arrays.

There are so many small differences that here we can put indexes in quotation marks as well. But if we were to use quotation marks in the array's index within the expression of a condition, put the index in apostrophes to avoid embedded quotation marks:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
    [több szavas]=10
)
 
# Beágyazott idézőjelek elkerüléséhez használhatunk aposztrófot a több szavas inxexeknél, de nem kötelező
[ "${tomb['több szavas']}" = 10 ] && echo "igaz" || echo "hamis"

Using Arrays in Functions

Unlike indexed arrays, the treating parameters as an array in dependencies, because with this method we can only "collect" the values ​​for the function. While indexes did not matter in indexed arrays, in this case our named keys are lost and only the values ​​"get" into the function. Let's see a similar example with an associative array:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
    [több szavas]=10
)
 
function fuggveny {
    tomb2=("$@")                    # Összes paraméter begyűjtése egy másik tömbbe
 
    for i in "${!tomb2[@]}"; do
        ertek=${tomb2[$i]}
        echo "$i: $ertek"
    done
}
 
# Függvény hívása a kibontott asszociatív tömbbel
fuggveny "${tomb[@]}"

output:

0: Harmadik elem
1: Második elem
2: Első elem
3: 10

As you can see in the output, the keys we created at the time of the declaration are lost.

The example program works fine anyway, doing exactly what it needs: When calling a function, a "$ {Array [@]}" we extracted the values ​​of the associative array with syntax, and this is what the function got. Which then created a new indexed array in line 10 with the values ​​obtained. So an indexed array copy was created from our associative array, the values ​​of which were retained, but the keys were lost and turned into serial numbers.

The good news, though, is that the name variable works nicely with the thing.

Parameter transfer by name

Previously familiar with the indexed array similar example works here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
    [több szavas]=10
)
 
function fuggveny {
    declare -n param1=$1
    # A declare parancs '-n' kapcsolója oldja meg a név szerinti hivatkozást
 
    for i in "${!param1[@]}"; do
        ertek=${param1[$i]}
        echo "$i: $ertek"
    done
}
 
# Függvény hívása csak a tömb változónak a nevével
fuggveny tomb

And the output is:

elem3: Harmadik elem
elem2: Második elem
elem1: Első elem
több szavas: 10

This way, both keys and values ​​are preserved.

Here, however, it should be noted that if we modify the variables created in this way in the function, it also affects the original variable in the global namespace of the program. An example is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash
 
# Tömb deklarálása a globális névtérben
declare -A tomb=(
    [elem1]="Első elem"
    [elem2]="Második elem"
    [elem3]="Harmadik elem"
)
 
function fuggveny {
    declare -n param=$1
 
    echo "Eredeti tömb tartalma a módosítás előtt:"
    for i in "${!param[@]}"; do
        ertek=${param[$i]}
        echo "$i: $ertek"
    done
 
    # A függvényen belül még hozzáfűzünk újabb elemeket a tömbhöz.
    param+=(
        [ujabb elem]="Függvényben adtuk hozzá"
        [ujabb elem2]="Ezt is a függvényben adtuk hozzá"
    )
}
 
# Függvény hívása a név szerinti paraméterrel
fuggveny tomb
 
echo
echo "Az eredeti tömb tartalma a globális névtérben a függvény lefutása után:"
 
for i in "${!tomb[@]}"; do
    ertek=${tomb[$i]}
    echo "$i: $ertek"
done

And the output is:

Eredeti tömb tartalma a módosítás előtt:
elem3: Harmadik elem
elem2: Második elem
elem1: Első elem

Az eredeti tömb tartalma a globális névtérben a függvény lefutása után:
elem3: Harmadik elem
elem2: Második elem
elem1: Első elem
ujabb elem: Függvényben adtuk hozzá
ujabb elem2: Ezt is a függvényben adtuk hozzá

You can see that the content of the original variable has changed after the function has been modified. This is because we created a name reference in the function to the original variable, not a new variable.

 

A next page we continue exporting arrays to global namespace ...

 

 

Navigation

This description consists of several pages: