notes sur l utilisation des array en bash shell

Mise à jour: 02/10/2023
Version: 1.0
Author: Jean-Louis Bicquelet-Salaün
Location: http://jlbicquelet.free.fr
Copyright: (c) 2023 Jean-Louis BICQUELET-SALAÜN

 utilisation des array en bash

Le langage bash propose l‘utilisation de tableaux (array). Cela permet un traitement itératif. Bien souvent, lorsqu'on script en bash on oublie cette fonctionnalité. Petit rappel.

Création d'un array

$ Array=(item1 item2 item3 "liste d'items")

Accéder à un élément de l‘array par son index

$ echo ${Array[0]}
item1
$ echo ${Array[2]} 
item3
i=3
$ echo ${Array[$i]}
liste d'items

Le premier élément du tableau a pour index 0.

Obtenir le nombre d'éléments du tableau

echo ${#Array[@]}
4

Afficher toutes les valeurs d‘un array

De façon globale

$ echo ${Array[*]}
item1 item2 item3 liste d'items

Boucler dans un array et l'afficher

for i in ${Array[*]}
do
 echo $i
done
item1
item2
item3
liste
d'items

Cette manière de faire ne donne pas forcément le résultat escompté. L'indice 4 qui contient un blanc n'est pas restitué comme on pourrait le penser.

Boucler dans un array avec un index explicite

C'est avec la manière globale la plus sur façon de faire.

$ for (( i=0; i<${#Array[@]}; i++ )); do    
  echo "n° $i : ${Array[$i]}"
done
n° 0 : item1
n° 1 : item2
n° 2 : item3
n° 3 : liste d'items

Note concernant la césure des mots

Bash a une variable interne $IFS, comme awk qui détermine la césure des mots.

Il faut faire attention à son contenu. Cela peut avoir une influence sur la gestion des noms d‘éléments dans une array.

Ne pas hésiter à regarder et àchanger sa valeur.

IFS='' correspond au blanc comme séparateur.

Par exemple, si on veut utiliser la virgule comme séparateur:

$ IFS=','
arr_nombres=(12,34,56)
$ echo ${arr_nombres[*]}
12 34 56
$ IFS=''

Ajouter un élément à un array

$ Array+=(item5)
jlb@mignon:~$ echo ${Array[*]}
item1 item2 item3 liste d'items item5

Supprimer un tableau

$ unset Array
$ echo ${Array[*]}

La ligne blanche confirme que le tableau a été supprimé.

Savoir si un élément de trouve dans un tableau

Solution itérative

C'est la solution à laquelle on pense en premier. On parcourt le tableau et on teste chaque élément du tableau pour voir si il correspond à la valeur.

Il vaut mieux écrire un script. Par exemple in array.bash.

#!/bin/bash
function array_contains() 
{
    local n=$#
    local value=${!n}
    for ((i=1;i < $#;i++)) {
        if [ "${!i}" == "${value}" ]; then
            echo "true"
            return 0
        fi
    }
    echo "false"
    return 1
}

lang=(awk c html makefile perltk python tk bash dialog js perl php tcl tkinter)


for j in "awk" "ada" "python" "lisp"
do 
  if [ $(array_contains "${lang[@]}" "$j") == "true" ]; then
    echo $j
  fi 
done
$ ./array.bash
awk
python

Cela fonctionne mais ce n'est pas la solution la plus optimsée.

Solution élégante

La solution élégante consiste à utiliser l'opérateur *=~ dans le tableau.

La syntaxe est de typr

if [[ " ${lang[@]} " =~ " ${j} " ]];

$ lang=(awk c html makefile perltk python tk bash dialog js perl php tcl tkinter)

for j in ada python lisp modula bash c c++
do 
  if [[ " ${lang[@]} " =~ " $j " ]]; then
    echo $j
  fi 
done

python
bash
c

Concatener deux array

a1=(chien chat canari)
$ a2=(lapin hamster souris)
$ a=("${a1[@]}" "${a2[@]}")
$ for i in "${a[@]}" ; do echo "$i" ; done
chien
chat
canari
lapin
hamster
souris

Trier un tableau

Méthode avec sort

Pour un tri numérique on utilise sort -n pour les nombres et sort pour les chaines.

$ nombres=(5 23 1 12 12) 
$ trie=($(printf "%s\n" ${nombres[*]} | sort -n)
$ echo ${trie[*]}
1 5 12 12 23

Les autres options sont possibles comme -rn pour reverse numérique, -r pour reverse, -h pour une vision "humaine" des tailles de disques et de fichiers, -u pour unique, -V pour trier par version.

Vous pouvez consulter le man de sort pour plus de possibilité.

$ nombres=(5 23 1 12 12) 
$ trie=($(printf "%s\n" ${nombres[*]} | sort -r)
$ echo ${trie[*]}
23 12 12 5 1
sorted=($(printf "%s\n" ${nums[*]} | sort -r))
$ echo ${sorted[*]}
5 23 12 12 1
$ disk_size=($(df | awk '{print $4}' | sort -h))
$ echo ${disk_size[*]}
Taille 5,0M 784M 785M 3,8G 3,9G 6,2G 8,1G 186G

On voit très dans l'exemple avec sort - que l'ordre correspond à l'ordre alphabetique et non l'ordre numérique.

On peut ausi utiliser IFS comme délimiteur.

$ rray=(item val list string)
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"

Tri avec un algorithme connu : bubble sort

Voici un exemple de script avec le très connu buble sort en utilisant une variable globale BSORT, bubble.bash .

declare BSORT=()

function bubble_sort()
{   #
    # @param [ARGUMENTS]...
    #
    # Sort all positional arguments and store them in global array BSORT.
    # Without arguments sort this array. Return the number of iterations made.
    #
    # Bubble sorting lets the heaviest element sink to the bottom.
    #
    (($# > 0)) && BSORT=("$@")
    local j=0 ubound=$((${#BSORT[*]} - 1))
    while ((ubound > 0))
    do
        local i=0
        while ((i < ubound))
        do
            if [ "${BSORT[$i]}" \> "${BSORT[$((i + 1))]}" ]
            then
                local t="${BSORT[$i]}"
                BSORT[$i]="${BSORT[$((i + 1))]}"
                BSORT[$((i + 1))]="$t"
            fi
            ((++i))
        done
        ((++j))
        ((--ubound))
    done
}

bubble_sort chat chien canari lapin souris
echo ${BSORT[@]}
/bubble.bash 
canari chat chien lapin souris