Administration Unix - CM séance 09
9. Approfondissements du langage bash (suite)
9.4. Commande declare
La commande interne declare
(voir : help declare
) permet de
- demander au shell des informations sur les variables et les fonctions (on appelle cette capacité l'introspection) ;
- mémoriser des attributs sur des variables ou fonctions (variable exportée, entière, liste, read-only...).
9.4.1. Introspection
$ toto="bonjour"
$ declare -p toto
declare -- toto="bonjour" # -- signifie fin des options
$ export toto
$ declare -p toto
declare -x toto="bonjour" # -x = attribut eXport
En fait, la commande declare
affiche ce qu'il faudrait taper pour créer
directement la variable :
$ declare -- titi="coucou"
$ echo "$titi"
coucou
$ declare -x tutu="byebye"
$ printenv tutu
byebye
On peut demander à declare
la liste des variables :
$ declare -p | tail -n 3
declare -- titi="coucou"
declare -x toto="bonjour"
declare -x tutu="byebye"
Ceci permet d'enregistrer toutes les variables :
$ declare -p >| sauvegarde.txt
et de les réutiliser plus tard :
$ source sauvegarde.txt
Grâce à declare
, on peut demander des informations sur des fonctions :
$ bingo() { echo "Vous avez gagné 1 million !" ;}
$ declare -f bingo # affiche le nom et le corps de la fonction
bingo ()
{
echo "Vous avez gagné 1 million !"
}
$ declare -F bingo # affiche uniquement le nom si c'est 1 fct
bingo
$ declare -F | tail -n 10 # affiche toutes les fonctions
...
declare -f bingo
...
On peut aussi exporter une fonction, avec export -f
ou declare -f -x
.
9.4.2. Attributs
La commande declare
permet d'activer une conversion automatique :
declare -u variable # convertit en majuscule lors de l'affectation
declare -l variable # idem en minuscule
Pour défaire, rappeler declare avec +u
ou +l
.
$ declare -u paf
$ paf=boum
$ echo $paf
BOUM
La commande declare permet de donner un "type" à une variable :
- type entier :
$ n=5
$ declare -p n
declare -- n="5" # variable ordinaire (chaîne de caractères)
$ n+=1 # à quoi sert += ?
$ declare -p n
declare -- n="51" # += a fait la concaténation : n="${n}1"
$ declare -i n # on donne le type entier (Integer)
$ n+=1
$ declare -p n
declare -i n="52" # += a fait l'addition : ((n+=1))
$ declare +i n # pour enlever un attribut on utilise +
$ n+=1
$ declare -p n
declare -- n="521" # c'est de nouveau une chaîne ⟶ concaténation
- types tableau - vus plus bas :
$ declare -a liste # tableau indexé
$ declare -A dict # tableau associatif
La commande declare permet aussi de créer une référence sur un nom de variable
avec l'attribut nameref
(c'est l'option -n
) :
$ a=flip
$ declare -n b=a # crée une référence b sur a
$ echo $b
flip
$ a=flop
$ echo $b
flop
$ b=flup
$ echo $a
flup
On peut obtenir le nom de la variable référencée :
$ echo ${!b} # "déréférence" ou "indirection"
a
On peut créer une référence vers une variable de tout type (y compris les tableaux indexés et associatifs).
À noter :
-
dans une fonction, on peut déclarer une variable locale de tout type, y compris une référence, en utilisant avec
local
les mêmes options quedeclare
:par exemple :local [option] nom[=valeur] ...
$ echanger() # a b { local -n a=$1 b=$2 # a et b sont des références local tmp tmp=$a ; a=$b; b=$tmp } $ c=ga ; d=bu $ echanger c d # on passe le nom des variables $ echo $c $d bu ga # ça les a bien échangé
ça ne marche que si les variables référencées sont accessibles (donc avec des noms différents).
-
on peut détruire une variable ou une fonction avec
unset
:unset variable unset -f fonction unset -n référence
9.5. Les tableaux
Caractéristiques :
- tableau à 1 dimension, de taille illimitée ;
- chaque case est une chaîne de caractère, de taille illimitée (on ne peut donc pas faire de tableau de tableaux).
bash gère 2 sortes de tableaux :
9.5.1. Les tableaux indexés
Particularités :
- cases indexées par des entiers
- tableau creux : les cases peuvent être non contigues
- peuvent être utilisés comme des listes
- déclaration optionnelle par :
declare -a
declare -a tab # crée un tableau tab
declare -a tab[$n] # idem, taille ignorée
tab[$i]=valeur # affectation, et création automatique de tab
${tab[$i]} # substitution par la valeur, "" si non affectée
Syntaxe : dans la substitution, les {}
sont obligatoires :
$ t[0]=ga ; t[1]=bu
$ echo $t # incorrect
ga
$ echo $t[1] # incorrect
ga[1]
$ echo ${t[1]} # ok
bu
Expansion arithmétique de l'indice entre []
:
même syntaxe que (())
, et on peut omettre les $
:
$ t[0]=ga ; t[1]=bu ; t[2]=zo ; t[3]=meu
$ i=1; echo "${t[i+1]}"
zo
On peut exprimer les éléments d'un tableau par une liste :
tab=() # vide le tableau
tab=(val1 val2 .. valn) # écrase le tableau, indices 0..n-1
tab=([2]=val1 [5]=val2 ..) # idem, pour les indices donnés
tab+=(valx ..) # concatène la liste à la fin de tab
Exemple :
$ t=(ba bu)
$ t+=(zo meu)
$ declare -p t
declare -a t='([0]="ba" [1]="bu" [2]="zo" [3]="meu")'
Récupération de la liste des valeurs : ${tab[*]}
ou "${tab[@]}"
⟶ même usage que $*
et "$@"
, pour itérer avec un for
.
$ t=(ga bu zo meu)
$ echo "${t[1]}"
bu
$ echo "${t[*]}"
ga bu zo meu
$ echo "${t[*]:2}"
zo meu
$ echo "${t[*]:1:2}"
bu zo
$ notes=(do "ré mi" fa)
$ for son in ${notes[*]} ; do echo "$son" ; done
do
ré
mi
fa
$ for son in "${notes[@]}" ; do echo "$son" ; done
do
ré mi
fa
Taille :
${#tab[i]} # longueur de la case i
${#tab[*]} # nombre de cases affectées
$ t=([2]=ga [8]=bu [5]=zo)
$ echo "${#t[*]}"
3
dans un tableau creux, taille du tableau != indice+1 du dernier élément
Liste des indices : ${!tab[*]}
$ t=([2]=ga [8]=bu [5]=zo)
$ echo ${!t[*]}
2 5 8
Boucler sur les indices d'un tableau creux :
$ for i in ${!t[*]}; do
echo "t[$i] = ${t[i]}"
done
t[2] = ga
t[5] = zo
t[8] = bu
Compléments :
unset tab # suppression du tableau tab
unset tab[i] # suppression de la case i
local -a tab # déclaration locale d'un tableau
read -a tab # lecture de l'entrée standard, découpe en mots,
# mémorisation des mots dans le tableau
args=("$@") # mémorise les arguments dans un tableau
b=("${a[@]}") # recopie le tableau a en le tassant
c=("${a[@]}" "${b[@]}") # concatène 2 tableaux en les tassant
9.5.2. Les tableaux associatifs
Les tableaux associatifs sont encore appelés hash table ou dictionnaire.
Permet de représenter des données structurées, des tables de bases de données... ⟶ outil très puissant (Javascript, Php, Python, ...)
Particularités :
- cases indexées par un mot clé
- déclaration obligatoire par :
declare -A
- même syntaxe que pour les tableaux indexés
Exemple :
$ declare -A dico
$ dico=([lundi]=poisson [mardi]=viande)
$ echo ${dico[mardi]}
viande
$ echo ${!dico[*]}
lundi mardi # clés
$ echo ${dico[*]}
poisson viande # valeurs
$ for cle in "${!dico[@]}"; do
echo "dico[$cle] = '${dico[$cle]}'"
done
dico[lundi] = 'poisson'
dico[mardi] = 'viande'
9.5.3. Passage de tableaux par référence
Possible en déclarant une référence de nom (nameref) avec declare -n
ou
local -n
(depuis la version 4.3 de bash en 2014).
afficher_case () # tableau cle
{
local -n tab="$1" # par référence
local cle="$2"
echo "${tab[$cle]}"
}
$ shadoks=(ga bu zo) # tableau indexé
$ afficher_case shadoks 1
bu
$ declare -A menu=([lundi]=poisson [mardi]=viande) # tableau associatif
$ afficher_case menu mardi
viande
modifier_case () # tableau cle valeur
{
local -n tab="$1" # par référence
local cle="$2" val="$3"
tab[$cle]="$val"
}
$ modifier_case shadoks 3 meu
$ declare -p shadoks
declare -a shadoks='([0]="" [1]="bu" [2]="zo" [3]="meu")'
$ modifier_case menu jeudi œuf
$ declare -p menu
declare -A menu='([lundi]="poisson" [jeudi]="œuf" [mardi]="viande" )'
le nom du tableau passé en référence doit être différent du nom local :
$ tab=()
$ afficher_case tab 0
bash: avertissement : tab: circular name reference