Mes documentations

« Retour à l'accueil

Manipulation de tableaux indicés en bash

Date de rédaction: août 2015
Dernière modification: août 2015

L’interpréteur bash permet d’utiliser des tableaux indicés ou associatifs à la syntaxe un peu cryptique, mais qui peuvent être pratiques quand on écrit des scripts shell un peu avancés. Mon introduction aux tableaux en bash présentait les opérations de base de leur utilisation. Cette fois, je présente quelques opérations courantes sur les tableaux indicés.

Sommaire

  1. Transformer une chaîne de caractères en tableau
  2. Transformer un tableau en chaîne de caractères
  3. Assigner les éléments d’un tableau à des variables
  4. Modifier tous les éléments d’un tableau en une opération
  5. Opération de base sur les lites
    1. Ajouter un élément en début de liste
    2. Ajouter un élément en fin de liste
    3. Supprimer le premier élément d’une liste
    4. Supprimer le dernier élément d’une liste
  6. Pour en savoir plus

1. Transformer une chaîne de caractères en tableau

Cette opération de transformation d’une chaîne de caractères en tableau est souvent disponible dans les languages de programmation sous la forme d’une fonction appelée « split ». Sous bash, on peut utiliser les fonctions de manipulation des chaînes de caractères et des tableaux pour transformer directement une chaîne de caractères en tableau.

Par exemple, pour assigner chaque chiffre d’une adresse IP à un élément de tableau :

$ ip="192.168.42.0"
$ tableau=( ${ip//./ } )
$ echo ${tableau[@]}
192 168 42 0
$ echo ${tableau[2]}
42

2. Transformer un tableau en chaîne de caractères

À l’inverse de « split », il y a la fonction couramment appelée « join » dans plusieurs languages de programmation qui permet de transformer une liste en chaîne de caractère. Sous bash, il y a plusieurs solutions, la plus élégante étant probablement la suivante :

$ tableau=( 192 168 42 0 )
$ ip=$(IFS="."; echo "${tableau[*]}")
$ echo $ip
192.168.42.0

Pour un peu plus d’efficacité au détriment du nombre de lignes, on peut écrire la même opération comme ceci (on évite ici la création du sous-processus effectuée par $()) :

$ tableau=( 192 168 42 0 )
$ OLD_IFS="$IFS"
$ IFS="."
$ ip="${tableau[*]}"
$ IFS="$OLD_IFS"
$ echo $ip
192.168.42.0

Dans les deux cas, il faut bien faire prendre soin d’utiliser ${tableau[*]}, et non ${tableau[@]} pour afficher tous les éléments du tableau, et de mettre cette expression entre guillements doubles. On tire ainsi partie de la notation ${tableau[*]}. Extrait du manuel de bash à ce sujet :

«  (…) entre guillemets doubles, ${nom[*]} se développe en un seul mot contenant les valeurs de chaque élément du tableau séparées par le premier caractère de la variable spéciale IFS et ${nom[@]} développe chaque élément de nom en un mot distinct.  »

Et dans le premier exemple ci-dessus, IFS n’est modifiée que dans le sous-processus créé par $(), mais dans le second, tout se passe dans le même processus, et on doit donc manuellement sauvegarder puis rétablir la valeur initiale de IFS pour qu’il n’y ait pas d’incidence sur les opérations effectuées par la suite dans le processus exécutant le shell.

3. Assigner les éléments d’un tableau à des variables

Cette opération n’est pas vraiment propre aux tableaux, mais peut être pratique quand on les utilise. Pour assigner les éléments d’un tableau à des variables, on peut utiliser la commande read combinée à une variante des documents en ligne, <<< :

$ tableau=( 192 168 42 0 )
$ read a b c d <<< "${tableau[*]}"
$ echo "a=$b b=$b c=$c d=$d"
a=192 b=168 c=42 d=0

Si le nombre de variables est inférieur au nombre d’éléments, la dernière variable reçoit la liste des éléments non assignés, séparés par une espace :

$ tableau=( 192 168 42 0 )
$ read a b c <<< "${tableau[*]}"
$ echo "c=$c"
c=42 0

Si, à l’inverse, le nombre de variables est supérieur au nombre d’éléments du tableau, les variables surnuméraires resteront simplement vides.

1. Modifier tous les éléments d’un tableau en une opération

Il est possible de modifier tous les éléments d’un tableau en une seule action grâce aux fonctions internes de bash de manipulation des chaînes de caractères. Lorsqu’elles sont appliquées sur un tableau, celles-ci agissent sur tous ses éléments.

À titre d’exemple, il est ainsi possible d’effectuer une substitution sur tous les éléments d’un tableau avec cette simple commande :

$ tableau=( "bender_is_smart" "bender_is_nice" "bender_rules" )
$ echo ${tableau[@]/bender/flexo}
flexo_is_smart flexo_is_nice flexo_rules

Pour connaître les autres fonctions de modifications de chaînes de caractères applicables aux tableaux, consultez la documentation « Manipulation des chaînes de caractères et des tableaux en bash ».

4. Opérations de base sur les listes

Un tableau indicé peut être utilisé comme une liste ordonnée et manipulé avec les opérations présentées ci-dessous à condition que chacune de ces opérations respecte ces deux règles  :

J’utilise ci-dessous le terme de liste pour désigner l’utilisation particulière d’un tableau indicé dans le respect de ces deux règles.

1. Ajouter un élément en début de liste

Une façon d’ajouter un élément en début de liste est de le recréer le tableau en préfixant son contenu actuel par l’élément à insérer.

$ declare -a tableau=( "un" "deux" )
$ tableau=( "zero" "${tableau[@]}" )
$ echo ${tableau[@]}
zero un deux

En recréant un tableau de même nom avec l’élément supplémentaire à son début, l’indice des anciens éléments est augmenté de un, et le nouvel élément est bien le premier, à l’indice 0.

Note: lors de la réinitialisation du tableau, les anciens éléments du tableau doivent impérativement être insérés sous la forme "${tableau[@]}", avec des guillements doubles et @ comme indice, et non *. Sans le respect de ces deux points, soit les éléments contenant des espaces seraient développés en plusieurs éléments, soit tous les éléments ne formeraient qu’un seul mot et seraient donc confondus en un seul élément.

2. Ajouter un élément en fin de liste

Pour ajouter un élément en fin de liste, on peut utiliser une solution similaire à l’ajout en début de liste dans laquelle on recrée le tableau en ajoutant le nouvel élément après ceux du tableau actuel :

$ declare -a tableau=( "zero" "un" )
$ tableau=( "${tableau[@]}" "deux" )
$ echo ${tableau[@]}
zero un deux

Note: comme précédemment, lors de la réinitialisation du tableau il faut impérativement utiliser la notation "${tableau[@]}", avec des guillements doubles et « @ » comme indice.

Une autre solution consiste à ajouter l’élément à la position fournie par le nombre d’éléments dans la liste (qu’on obtient avec ${#tableau[@]}).

$ declare -a tableau=( "zero" "un" )
$ tableau[${#tableau[@]}]="deux"
$ echo ${tableau[@]}
zero un deux

Note : la taille du tableau est bien la position après le dernier élément, puisque les tableaux sont indicés à partir de 0 et qu’on suppose dans le cadre de leur utilisation en liste que les indices sont tous consécutifs.

3. Supprimer le premier élément d’une liste

Selon la première règle à respecter, le premier élément de la liste est à l’indice 0, et comme l’indique mon « Introduction aux tableaux en bash, la suppression d’un élément se fait avec la commande unset. Cependant, après la commande unset tableau[0], le premier élément se trouve à l’indice 1.

Pour continuer à respecter la règle du premier élément à l’indice 0, il faut changer l’indice des autres éléments pour les diminuer de un. Ce changement d’indices peut être obtenu simplement en réinitialisant le tableau avec les mêmes éléments :

$ tableau=( "zero" "un" "deux" )
$ unset tableau[0]
$ tableau=( "${tableau[@]}" )
$ echo ${tableau[*]}
un deux
$ echo ${!tableau[*]}
0 1

Note: comme plus haut, lors de la réinitialisation du tableau, il faut impérativement utiliser la notation "${tableau[@]}", avec des guillements doubles et « @ » comme indice.

4. Supprimer le dernier élément d’une liste

En supposant que le tableau respecte les règles des listes ordonnées énoncées plus haut, le dernier élément se trouve à l’indice taille - 1. Il suffit donc de le supprimer simplement avec unset :

$ tableau=( zero un deux )
$ unset tableau[$(( ${#tableau[*]} - 1 ))]
$ echo ${tableau[*]}
zero un

Note: contrairement aux opérations de lecture, avec unset on ne peut pas utiliser la notation tableau[-1] pour désigner le dernier élément.

3. Pour en savoir plus

Dernières modifications de ce document