Nous avons découvert les tableaux dans le chapitre Tableaux. Ce document présente d'autres façons de les utiliser.
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"
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 "$*"
}
(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.
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")
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.
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.
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).
À 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"
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
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.
$1, $2, etc."$@"
"$*"
$#
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.