Parcours
/
Bash
Bash
/
Programme
/
Pour en savoir plus sur les tableaux
Po

Pour en savoir plus sur les tableaux en Bash

{one: "%{count} exercice", many: "%{count} d'exercices", other: "%{count} exercices"}

À propos de Pour en savoir plus sur les tableaux

Nous avons découvert les tableaux dans le chapitre Tableaux. Ce document présente d'autres façons de les utiliser.

Concaténer les éléments d'un tableau en une seule string

Dans le chapitre précédent sur les tableaux, tu as vu "${myarray[@]}", avec l'indice @, qui sert à développer le tableau en chacun de ses éléments. Mais parfois, tu veux rassembler tous les éléments en une seule string. Pour cela, utilise l'indice * :

echo "${myarray[*]}"

Il est obligatoire d'entourer l'expansion de guillemets doubles.

Bash utilise le premier caractère de la variable interne IFS comme caractère de séparation. Par défaut, $IFS se compose de l'espace, de la tabulation et du saut de ligne.

myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"

On peut modifier la variable IFS pour utiliser un autre caractère de séparation :

myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
Advanced
On peut encapsuler tout ça dans une fonction. Clique pour voir les détails.
join() {
    local IFS=$1
    shift
    local elements=("$@")
    echo "${elements[*]}"
}

join ":" "${myarray[@]}"     # note, the "@" index
# => "one:two:three:four"

Rendre IFS local à la fonction évite de devoir sauvegarder son ancienne valeur pour la restaurer ensuite dans la portée globale.

Autre raffinement : le paramètre spécial "$*", une fois entre guillemets, joue le même rôle, ce qui évite de garder une copie des arguments de la fonction :

join() {
    local IFS=$1
    shift
    echo "$*"
}
Sans passer par une fonction, modifier IFS dans un sous-shell est un bon moyen d'éviter de le modifier dans le shell courant. Clique pour voir les détails.
(IFS=","; echo "${myarray[*]}")

Les parenthèses créent un sous-shell (une copie du shell courant). Quand les commandes situées entre les parenthèses se terminent, le sous-shell se ferme et la variable IFS modifiée disparaît.

Attention, ceci ne fonctionnera pas : IFS="," echo "${myarray[*]}", car l'expansion des paramètres est effectuée en premier, avant que le shell n'applique la variable IFS modifiée à la commande echo.

Tranches de tableau

Tu as peut-être déjà vu l'expansion de paramètres "${variable:offset:length}" qui développe une sous-chaîne de la valeur de la variable. On peut faire la même chose avec les tableaux, pour développer une tranche du tableau.

myarray=(one two three four)

subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")

subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")

Omettre la longueur signifie « depuis le décalage jusqu'à la fin du tableau » :

subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")

Passer un tableau à une fonction

Ce n'est pas aussi simple que dans les autres langages que tu connais peut-être. Il existe deux techniques principales pour passer un tableau à une fonction.

Passer les éléments

Dans la première technique, on passe toutes les valeurs du tableau et on les rassemble dans un tableau local à l'intérieur de la fonction.

myfunc() {
    local array_copy=("$@")
    # do stuff with array_copy
    declare -p array_copy
}

array_original=(11 22 33 44)
myfunc "${array_original[@]}"

Le tableau de la fonction contient une copie des valeurs. Les modifications apportées au tableau dans la fonction ne se répercutent pas sur la portée extérieure.

Passer le nom du tableau

Cette technique se rapproche de la fonctionnalité de « passage par référence » que tu connais peut-être dans d'autres langages. On passe le nom du tableau sous forme de string. La fonction crée alors une variable locale dotée de l'attribut nameref. Ce tableau local et le tableau global (dont on a passé le nom) sont le même tableau.

myfunc() {
    # note the `-n` option
    local -n local_array=$1

    # do stuff with local_array
    for i in "${!local_array[@]}"; do
        printf '%d => %s\n' "$i" "${local_array[i]}"
    end

    # we can mutate it
    local_array+=(55 66 77)
}

array_original=(11 22 33 44)
myfunc "array_original"

# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")

Les namerefs fonctionnent aussi avec les tableaux associatifs, ainsi qu'avec les variables « scalaires » (qui contiennent une valeur de type string).

Note

À l'intérieur de la fonction, declare -p local_array n'est pas très utile. Il se contente d'afficher declare -n local_array="array_original". Pour obtenir des informations détaillées sur le tableau, tu peux inspecter le nom du tableau passé en argument : declare -p "$1"

Caution

Fais attention à ce que le tableau local porte un nom différent de celui du tableau passé en argument. Le code fonctionnera quand même, mais il émettra des avertissements de « circular name reference » comme ceci :

myfunc() {
    local -n a=$1
    local IFS=,
    echo "${a[*]}"
}

# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three

Les paramètres positionnels ressemblent à un tableau

Dans les shells qui ne visent à respecter que la norme POSIX (comme ash et dash), il n'y a pas de tableaux. Ce qui s'en approche le plus, ce sont les paramètres positionnels.

  • Les paramètres positionnels sont accessibles par leur indice : $1, $2, etc.
  • On les développe en éléments individuels avec "$@"
  • On les concatène en une seule string avec "$*"
  • Le nombre de paramètres est $#

Utilise la commande set pour leur affecter des valeurs :

set -- one two three
set -- "$@" four

for item in "$@"; do
    echo "do something with $item"
done

Si ton objectif est d'écrire des scripts shell « portables », tu utiliseras les paramètres positionnels pour stocker un « tableau » de valeurs.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet