Faire du JSON avec PHP ou tout autre langage orienté web, c’est trivial. Mais lorsqu’il faut commencer à se le coltiner en Bash, c’est déjà plus galère..
Pour un besoin personnel, je dois justement lire du JSON dans un shell. Le cahier des charges est simple : J’ai PHP et Python sur la bécane (un serveur), et dans l’optique de partager ce code plus tard, je ne souhaite pas avoir de dépendances. Exit donc jq, qui est une librairie permettant de combler largement mon besoin. En plus, je ne connais pas grand chose à Python, et j’ai franchement pas le temps de m’y mettre.
Dans ce billet, je vous montre comment exploiter du JSON en Bash avec PHP. Je tiens tout de même à préciser que cet article n’a pas vocation à vous apprendre ces 2 langages, mais suppose que vous en connaissiez déjà les bases.

Je vous préviens, c’est long, mais c’est également l’occasion de voir quelques astuces Bash (notamment sur les tableaux) que peut-être vous ne connaissiez pas.

Petit aparté

J’avais dit vouloir prendre un peu de recul par rapport à ce blog, et me recentrer. Je continue donc le site dont je vous avais parlé, mais il est difficile pour moi de ne pas écrire ici dès lors que j’ai le clavier sous la main, dès que quelque chose me trotte dans la tête.
Aujourd’hui, reprise du boulot, je lâche ma tasse de café pour rédiger quelques lignes ici. On ne se refait pas. Et puis, je ne m’étais jamais imposé de rythme, alors autant continuer comme ça.
Oui, je change d’avis comme de chemise. Ou plutôt, je fais comme bon me semble, comme j’ai toujours fait. Si demain je n’ai pas envie de continuer cet article, qui ira me dire quelque chose ?

Je crois que je ne sais pas comment évoluera mon temps passé sur le PC. Il y a fort à parier qu’à terme les extras ne se fassent plus. Je consultais de temps à autre Mastodon, mais il ne s’agit pour la plupart que de geeks / libristes qui exposent leurs articles, des liens vers un tweet, une humeur à partager. Je n’ai pas besoin de ça.
Au contraire, je pense qu’en fait ce blog est devenu le centre de mon activité sur Internet, et que le reste ne fait que graviter autour. L’avenir nous le dira.

Utiliser PHP en Bash

Pour revenir à nos moutons, nous souhaitions exploiter du JSON en Bash avec PHP. On va tenter d’y aller étape par étape. Il s’agit tout d’abord d’exécuter un script PHP depuis un script Bash ou directement depuis un shell.

Nous partirons sur 2 fichiers : un script Bash, l’autre un PHP, tous 2 dans le même dossier.

Voilà le contenu du fichier ‘disks.php’, qui ne fera que retourner une valeur au script Bash :

<?php
echo 5;

À présent, le fichier ‘test.sh’, le fichier Bash :

#!/bin/bash
echo "Nombre de disques : " $(php -f $(dirname $0)'/disks.php')

Si vous lancez à présent le script ‘test.sh’, que vous aurez rendu exécutable auparavant, vous aurez ceci :

Nombre de disques :  5

J’explique :
Pour lancer un script PHP depuis Bash, il suffit de faire :

php -f chemin/du/script.php

php est la commande permettant de lancer la fonction PHP, que vous aurez installé bien sûr…

L’option -f indique qu’on souhaite exécuter un fichier et non pas une commande.

Le dernier argument est le chemin et le nom du script à traiter.

Au cas où, dirname $0 permet de récupérer le chemin absolu du script courant.

Enfin, il est bon de savoir que tout ce qui sortira du script PHP (les echo, print etc), ne seront pas affichés. Ils seront simplement retournés au script Bash.
On pourrait ainsi réécrire la partie Bash comme ceci, sans toucher au PHP :

#!/bin/bash
nbDisks=$(php -f $(dirname $0)'/disks.php')
echo "Nombre de disques : $nbDisks"

# Nombre de disques : 5

Ça ne changerait rien, PHP n’affiche rien. La valeur 5 serait simplement affectée à la variable $nbDisks , qu’on afficherait ensuite.

Arguments

Tout ça c’est bien, mais c’est trop scolaire. À quoi sert d’appeler un script si on ne peut pas lui passer d’arguments ? Pas grand chose, en effet.
Il y a 2 méthodes pour passer des arguments à PHP depuis Bash, que nous allons voir.

Des arguments simples

Il s’agit de passer un seul argument, une seule valeur au script à exécuter.

Dans l’exemple qui va suivre, je souhaite récupérer en Bash le nombres d’élèves de certaines classes. Si le nom d’un professeur est renseigné, on va chercher le nombre d’élèves de sa classe (on peut imaginer une requête SQL depuis PHP par exemple). Si aucun professeur n’est trouvé, on indique une erreur.
Enfin, si aucun nom n’est indiqué, on retourne le nombre d’élèves de toutes les classes.

<?php

if ($argc === 1) {
    // Aucun argument n'est donné, on donne tous les élèves
    echo '62';
} else {
    if ($argv[1] === 'michu') {
        // Classe de Mme Michu
        echo '28';
    } elseif ($argv[1] === 'foo') {
        // Classe de Foo
        echo '34';
    } else {
        echo 'Pas de classe avec ce professeur';
    }
}

2 points tout de même :

  • $argv est un tableau contenant les arguments passés au script lorsque PHP est utilisé en CLI. Quelque soit le nombre d’arguments, $argv[0] contient le chemin et le nom du script en cours.
  • $argc  contient le nombre d’arguments passés au script. Lorsque aucun argument n’est donné, cette variable est égale à 1.

Passons au script Bash :

#!/bin/bash

echo "Nombre d'élèves dans la classe de Mme Michu : "$(php -f $(dirname $0)'/students.php' 'michu')
echo "Nombre d'élèves dans la classe de Foo : "$(php -f $(dirname $0)'/students.php' 'foo')
echo "Nombre d'élèves total : "$(php -f $(dirname $0)'/students.php')
echo "Nombre d'élèves chez Bar : "$(php -f $(dirname $0)'/students.php' 'bar')

Le résultat :

Nombre d'élèves dans la classe de Mme Michu : 28
Nombre d'élèves dans la classe de Foo : 34
Nombre d'élèves total : 62
Nombre d'élèves chez Bar : Pas de classe avec ce professeur

À noter qu’il est possible de passer plusieurs arguments avec cette méthode, simplement en les espaçant :

echo $(php -f $(dirname $0)'/students.php' 'michu' 'plop' 55 'baz')

Cette méthode permet de passer plusieurs valeurs simplement, mais elle ne couvre pas tous les besoins.

Bon à savoir : Chaque argument est envoyé comme une chaîne de caractères, qu’il soit entouré de guillemets ou pas.
Aussi, dans l’exemple précèdent, le ’55’ passé au script sera de type string lorsqu’il sera utilisé par PHP.

Arguments associatifs

Il existe une astuce que j’ai découvert il y a peu, c’est de pouvoir passer des paramètres avec un nom et une valeur à un tel script. Quelle utilité à vrai dire ?
Dans l’exemple précèdent, avec les classes et le nombre d’élèves, comment passer au script que la classe de Mme Michu possède maintenant 30 élèves ?

Et bien il suffit de passer les paramètres précédés d’un double tiret --  et de les assigner comme suit :

#!/bin/bash

echo "$(php -f $(dirname $0)'/disks.php' -- 'nom=Mme Michu' eleves=30)"

Coté PHP, nous allons toujours utiliser les variables $argc et $argv. Cependant, pour pouvoir les utiliser correctement, il va falloir faire une petite manipulation :

<?php

print_r($argv);

if ($argc === 1) {
    // No argument
} else {
    $args = [];
    for ($i = 1; $i <= $argc - 1; $i++) {
        // Each args
        $strArgs = explode('=', $argv[$i]);
        $args[$strArgs[0]] = $strArgs[1];
    }
}

if (isset($args)) {
    echo $args['nom'] . ' a dans sa classe ' . $args['eleves'] . ' élèves';
}

Une fois le script Bash lancé, le résultat sera celui-ci :

Array
(
    [0] => /home/maxk/bin/students.php
    [1] => nom=Mme Michu
    [2] => eleves=30
)
Mme Michu a dans sa classe 30 élèves

Je n’explique pas le print_r($argv), je pense que vous comprendrez. En revanche, on voit clairement que pour exploiter les arguments, il va falloir séparer le nom de l’argument (nom et eleves) de leurs valeurs (Mme Michu et 30).

C’est ce que fait le script ensuite : Il explose chaque argument s’il y en a avec le signe ‘=’, et on se retrouve avec un tableau $args qui contient les arguments exploitables. Pas beau ça ?

Variables globales

Avant d’attaquer les choses sérieuses, je voulais vous parler d’un petit truc qui m’est bien utile.
Dans mes scripts, j’ai régulièrement besoin de passer des variables de Bash à PHP. Je peux utiliser les arguments décrits ci-dessus bien sûr, mais il existe des cas où cela devient redondant.
C’est le cas par exemple lorsqu’il s’agit de passer encore et toujours la même valeur, la même variable.

En Bash

Il est possible de passer, en Bash, des variables en globales, c’est à dire que cette variable sera connue de tous les scripts qui seront exécutés.

Vous connaissiez cette façon pour assigner une valeur à une variable :

foo='je suis locale'

Et bien, il y a une autre façon de déclarer une variable pour qu’elle soit globale, qu’elle puisse être utilisée dans tous les scripts lancés depuis le processus :

export bar='je suis globale'

La variable bar  est ici accessible depuis n’importe quel script ! Essayons cela.
Créez un fichier qui va déclarer 2 variables, une locale et une globale, et qui lancera un autre script Bash :

#!/bin/bash

foo='je suis locale'
export bar='je suis globale'

$(dirname $0)'/export.sh'

Puis créez le second fichier, export.sh, qui sera appelé depuis le premier script, et qui aura pour simple fonction d’afficher le contenu de ces 2 variables :

#!/bin/bash

echo $foo
echo $bar

Lançons le premier script (test.sh) et observons le résultat :

je suis globale

Seul le résultat de $bar est affiché, car $foo  n’est pas connu du script export.sh. En revanche, Bash ne lève aucune erreur si une variable n’existe pas, elle sera simplement considérée comme une chaîne de caractères vide.

En PHP

À présent, voyons comment récupérer une variable globale déclarée en Bash depuis PHP.
Cela est possible grâce à la fonction getenv(), qui permet de récupérer une variable de l’environnement.

Comme tout à l’heure, nous allons déclarer une variable globale dans notre script, et appeler un script PHP, qui devra l’afficher :

#!/bin/bash

foo='je suis locale'
export bar='je suis globale'

echo $(php -f $(dirname $0)'/disks.php')

Et le script PHP :

<?php

$varFoo = getenv('foo');
$varBar = getenv('bar');

echo "Foo : $varFoo\\r";
echo "Bar : $varBar\\r";

Lançons le script Bash, et voyons le résultat :

Foo : 
Bar : je suis globale

Il est possible de récupérer les variables de Bash en PHP. Génial non ?
À savoir qu’une fois la variable déclarée avec export , il est possible de la modifier à souhait en Bash, sans la précéder de export , elle sera toujours globale.

Du JSON en Bash avec PHP

Maintenant que nous avons vu qu’utiliser PHP avec Bash n’était pas bien difficile, il est l’heure de nous intéresser au sujet important : Exploiter du JSON en Bash avec PHP.

Dans les exemples qui vont suivre, pour éviter les répétitions, nous partirons du principe que notre fichier JSON system.json contient ceci :

{
    "host": "Debian 8.9",
    "kernel": "3.16.0-4-586",
    "packages": {
        "yunohost": "2.6.4",
        "yunohost-admin": "2.6.1",
        "moulinette": "2.6.1",
        "ssowat": "2.6.8"
    },
    "system": {
        "disks": {
            "sda7": "Mounted on /tmp, 360.0MiB (358.0MiB free)",
            "sda5": "Mounted on /var, 2.7GiB (1.7GiB free)",
            "sda8": "Mounted on /home, 131.3GiB (130.8GiB free)",
            "sda1": "Mounted on /, 8.1GiB (6.7GiB free)"
        },
        "memory": {
            "ram": "2.0GiB (1.5GiB free)",
            "swap": "4.0GiB (4.0GiB free)"
        }
    },
    "apps": {
        "installed": "Firefox",
        "installed": "Netbeans",
        "installed": "VLC"
    }
}

Récupérer une valeur

Le plus simple pour démarrer, c’est de demander à PHP de nous retourner une simple valeur contenue dans ce fichier JSON.
Pour cela, il nous faudra en Bash fournir un argument : La clé que nous souhaitons récupérer. Le script PHP s’occupera de nous retourner le résultat.

Je vous donne le code complet du script PHP (je l’ai appelé system.php). Je me suis aidé des trucs que l’on a vu plus haut, et je vous explique le reste ensuite :

<?php

// Récupération du chemin du fichier JSON
$path = getenv('ROOT_PATH');
$jsonFile = $path . '/system.json';

// Explosion des arguments
if ($argc === 1) {
    // Pas d'argument
} else {
    $args = [];
    for ($i = 1; $i <= $argc - 1; $i++) {
        // Pour chaque argument, on explose son nom et sa valeur
        $strArgs = explode('=', $argv[$i]);
        $args[$strArgs[0]] = $strArgs[1];
    }
}

// Récupération et décodage du fichier en un objet json
$system = json_decode(file_get_contents($jsonFile));

// Si une clé est spécifiée, on tente d'y accèder
if (isset($args['key'])) {
    $parts = explode("->", $args['key']);
    foreach ($parts as $part) {
        if (isset($system->$part)) {
            $system = $system->$part;
        }
    }
}

// On retourne le résultat
echo $system;

Au début, on voit que je fais appel à la variable ROOT_PATH  qui sera définie dans le script Bash. Elle nous servira en PHP à aller récupérer le chemin du fichier JSON.
Puis on définit le chemin du fichier JSON à utiliser.

La partie suivante est expliquée en haut, jusqu’à la récupération du fichier JSON, que l’on décode en un objet JSON. Si vous n’êtes pas à l’aise avec l’utilisation de JSON en PHP, je ne peux que vous conseiller cet excellent article sur le sujet.

Enfin, nous allons ‘découper’ chaque partie de la clé si elle est fournie, en cherchant les éventuels ‘->’ présents, et tenter de récupérer la valeur correspondante dans le JSON. Par exemple en fournissant la clé ‘system->memory->ram’, le script va découper celle-ci en 3 parties et tenter d’accéder successivement à : ‘$system->system’, puis ‘$system->system->memory’, et enfin ‘$system->system->memory->ram’.
Si celle-ci existe, sa valeur est retournée, sinon, on retourne la dernière valeur trouvée.

L’utilisation de ce script avec Bash est donc toute aisée, puisqu’il suffit de définir le dossier courant du script, la variable globale ROOT_PATH , et la clé de la valeur à récupérer avec le nom ‘key’ :

#!/bin/bash

export ROOT_PATH=$(dirname $0)
echo "Version du kernel : " $(php -f $ROOT_PATH'/system.php' 'key=kernel')
echo "RAM : " $(php -f $ROOT_PATH'/system.php' 'key=system->memory->ram')

Résultat :

Version du kernel :  3.16.0-4-586
RAM :  2.0GiB (1.5GiB free)

Pas bien ça ? Si, c’est bien, mais pas tant que ça.
Imaginez que vous souhaitez récupérer tous les packages contenus dans le fichier JSON. Si vous ne les connaissez pas à l’avance, c’est impossible avec cette méthode.
C’est ce que nous allons voir tout de suite.

Récupérer un tableau

Si à première vue il est assez facile de passer un tableau de PHP à Bash, en réalité c’est tout autre. Comme vous le savez (ou le découvrez), PHP ne peut retourner que du texte à Bash. Il va donc falloir ruser pour récupérer un tel tableau.

Tableau indexé

Il s’agit simplement de récupérer une liste, sans notion d’association, de valeurs. On souhaite par exemple récupérer les applications installées dans notre fichier JSON, à savoir Firefox, Netbeans et VLC.

Comme dit juste au dessus, le problème est que le retour de PHP à Bash ne se fait que par une chaîne de caractères. Pour en extraire un tableau, il va donc falloir trouver un délimiteur, un truc qui va séparer chaque valeur.
Ce séparateur peut être plein de choses, comme un espace, un retour à la ligne, une lettre, …Dans tous les cas, il faut être certain de ne pas croiser ce caractère dans les données JSON à traiter.
Pour ma part, comme je sais que j’aurai éventuellement des espaces, des tabulations et des sauts de ligne, j’ai choisi d’utiliser un symbole que je ne croiserai certainement pas : ¶

Si l’on est sûr que les valeurs du tableau ne contiennent pas ce caractère spécial, on peut donc l’utiliser pour en séparer les valeurs.

On va modifier un peu notre script PHP pour que, si la clé demandée contient un tableau (objet StdClass de notre JSON), il nous retourne le tableau sous forme de chaîne de caractère, en séparant les valeurs par le caractère choisi plus haut :

<?php

// Récupération du chemin du fichier JSON
$path = getenv('ROOT_PATH');
$jsonFile = $path . '/system.json';

// Explosion des arguments
if ($argc === 1) {
    // Pas d'argument
} else {
    $args = [];
    for ($i = 1; $i <= $argc - 1; $i++) {
        // Pour chaque argument, on explose son nom et sa valeur
        $strArgs = explode('=', $argv[$i]);
        $args[$strArgs[0]] = $strArgs[1];
    }
}

// Récupération et décodage du fichier
$system = json_decode(file_get_contents($jsonFile));

// Si une clé est spécifiée, on tente d'y accèder
if (isset($args['key'])) {
    $parts = explode("->", $args['key']);
    foreach ($parts as $part) {
        if (isset($system->$part)) {
            $system = $system->$part;
        }
    }
}

if (is_object($system)) {
    // La clé contient plusieurs valeurs
    $str = ""; // On initialise une chaîne vide
    foreach ($system as $key => $value) {
        $str .= $value . '¶';
        // On colle chaque valeur dans la chaîne et un séparateur
    }
    $system = $str;
    // On attribue cette chaîne à notre variable qui sera retournée au script Bash
}

// On retourne le résultat
echo $system;

C’est ce bout de code qui va permettre de retourner toutes les valeurs si la clé passée ne contient pas qu’une seule valeur mais plusieurs.

Rien de compliqué là-dedans, vous devriez vous en sortir avec les commentaires. Passons au Bash :

#!/bin/bash

export ROOT_PATH=$(dirname $0)

declare -a apps

IFS='¶' read -a apps <<<$(php -f $ROOT_PATH'/system.php' 'key=apps')

for app in "${apps[@]}"
do
    echo $app
done

echo "Mon préféré est ${apps[1]}"

Plusieurs choses à noter ici :

  • declare -a apps  : On déclare la variable apps comme un tableau indexé.
  • IFS : C’est une variable du Shell, qui définit le séparateur. Par défaut, elle contient un espace, une tabulation (\\t)et un retour à la ligne (\\n). Chaque caractère qu’elle contient est un séparateur à lui-seul, il n’est pas possible d’utiliser une suite de caractères.
    En le faisant suivre d’une autre instruction, le séparateur est modifié uniquement pour cette instruction.
  • read  : Attend une réponse de l’utilisateur, comme un prompt
  • -a apps : Détermine le nom de la variable dans laquelle le résultat de read sera stockée.
  • <<< : Permet de ne pas attendre que l’utilisateur tape quelque chose, mais va envoyer la résultat du script PHP à l’instruction read, comme si l’utilisateur l’avait tapé lui-même dans le Shell.
  • On lance le script PHP system.php en donnant la clé ‘apps’, pour récupérer les applications de notre fichier JSON.

À l’issue de la commande read …, la variable $apps  contient un tableau indexé avec les valeurs Firefox, Netbeans et VLC.

Puis on parcours simplement le tableau et on affiche chaque valeur.
Enfin, on affiche uniquement l’élément du tableau qui a pour index 1. Pour mémoire, les index commencent à 0.

Si on lance le script, voilà le résultat :

Firefox
Netbeans
VLC
Mon préféré est Netbeans

Tableaux associatifs

On touche au but, mais également la partie la plus chiante.
Toujours dans notre fichier JSON de départ, comment pourriez-vous associer les disques (sda7, sda5 etc) à leurs mémoires respectives ?
Depuis Bash 4, il est possible d’utiliser des tableaux associatifs. Et vous allez voir que ça vaut le coup de se faire chier un peu, car on va commencer à pouvoir profiter pleinement de notre fichier JSON.

Mais le problème rencontré à la création d’un simple tableau est double. Il va maintenant falloir séparer chaque entité du tableau, mais également séparer la clé de sa valeur. Pour cela j’ai utilisé une solution toute simple : J’ai pris un second caractère spécial : þ . Ainsi dans le script que PHP doit me retourner, un tableau va ressembler à ça :

clé1þvaleur1¶clé2þvaleur2¶...

Et Bash s’occupera de découper cela.
Mais il y a un autre soucis : Comment faire savoir à PHP que l’on souhaite récupérer un tableau associatif, et non pas un tableau indexé ?
Et bien nous allons encore une fois utiliser un argument, que l’on nommera ‘assoc’. Si celui-ci est passé à 1, alors PHP devra retourner un tableau associatif, sinon un tableau indexé.

Vous y êtes ? C’est parti !

<?php

// Récupération du chemin du fichier JSON
$path = getenv('ROOT_PATH');
$jsonFile = $path . '/system.json';

// Explosion des arguments
if ($argc === 1) {
    // Pas d'argument
} else {
    $args = [];
    for ($i = 1; $i <= $argc - 1; $i++) {
        // Pour chaque argument, on explose son nom et sa valeur
        $strArgs = explode('=', $argv[$i]);
        $args[$strArgs[0]] = $strArgs[1];
    }
}

// Récupération et décodage du fichier
$system = json_decode(file_get_contents($jsonFile));

// Si une clé est spécifiée, on tente d'y accèder
if (isset($args['key'])) {
    $parts = explode("->", $args['key']);
    foreach ($parts as $part) {
        if (isset($system->$part)) {
            $system = $system->$part;
        }
    }
}

if (is_object($system)) {
    // La clé contient plusieurs valeurs
    $str = ""; // On initialise une chaîne vide
    foreach ($system as $key => $value) {
        if (isset($args['assoc']) && $args['assoc'] === '1') {
            // Tableau associatif
            // On colle chaque clé et valeur dans la chaîne
            // La clé est séparée de sa valeur
            $str .= $key . 'þ' . $value . '¶';
        } else {
            // Tableau indexé
            $str .= $value . '¶';
            // On colle chaque valeur dans la chaîne et un séparateur
        }
    }
    $system = $str;
    // On attribue cette chaîne à notre variable qui sera retournée au script Bash
}

// On retourne le résultat
echo $system;

En gros si l’argument ‘assoc’ est passé à 1, on insère dans la chaîne de retour la clé et la valeur, avec les séparateurs choisis. Sinon, on n’insère que la valeur.

Passons au Bash à présent, ça risque d’être un peu long…

#!/bin/bash

export ROOT_PATH=$(dirname $0)

# On déclare le tableau indexé tmp
declare -a tmp

# On récupère les disques et on demande au script PHP un tableau associatif
IFS='¶' read -a tmp <<<$(php -f $ROOT_PATH'/system.php' 'key=system->disks' 'assoc=1')

# Déclaration du tableau associatif disks
declare -A disks

# On parcourt les disques stockés dans la variable $tmp
for disk in "${tmp[@]}"
do
    # On explose la clé de sa valeur à l'aide du second séparateur
    IFS='þ' read -a arr <<<$disk
    # On associe la clé et la valeur dans le tableau associatif
    # disks[cle]=valeur
    disks[${arr[0]}]=${arr[1]}
done

for K in "${!disks@]}"
do
    echo $K --- ${disks[$K]}
done

# Déclaration d'un tableau pour les clés
declare -a keys
# On récupère toutes les clés des disques
keys=(${!disks[@]})

echo "Ma partition /var se nomme ${keys[2]} et est ${disks[${keys[2]}]}"

echo "Mais je peux utiliser ça aussi : ${disks['sda7']}"

Pratiquement tout a été modifié depuis l’étape plus haut.
J’ai modifié la variable $apps en $tmp , car ce tableau n’est maintenant que temporaire : Une fois le script PHP exécuté, c’est un tableau contenant les clés et les valeurs sous cette forme :

clé1þvaleur1
clé2þvaleur2

Remarquez qu’on déclare ensuite le tableau $disks . Ce n’est pas une faute de frappe, j’ai volontairement mis un A majuscule, à l’instar de $tmp . Et oui, un tableau associatif sous Bash se déclare de telle façon :

declare -A montableau

Puis on parcourt les disques (la variable $tmp ), dont on va extraire la clé et la valeur.
Pour cela, on utilise encore la méthode du read <<<  , avec un autre séparateur, le second (ici þ ). La clé et la valeur sont donc stockées dans un autre tableau temporaire, $arr  , puis on stocke ces 2 informations dans notre tableau associatif $disks  .
Vous suivez encore ?

Une fois la boucle for disk in ... terminée, le tableau $disks est rempli. Nous pouvons donc le parcourir afin d’y afficher son contenu. C’est ce que fait la boucle for K ...   qui suit.

Pour info, le ‘!’ dans l’instruction ${!disks@]} signifie que l’on souhaite regarder dans les clés du tableau associatif. Le « @ » est utilisé pour récupérer toutes les données. On peut donc traduire vulgairement :

for K in "${!disks[@]}" 

Par : « Pour toutes les clés appelées K trouvées dans le tableau $disks »

Mais… À ce stade, il est impossible d’afficher un seul élément du tableau, ni la clé ni sa valeur (à moins de connaître la clé). Si vous pensiez pouvoir faire ceci :

echo "${!disks[1]}" # Incorrect

Et bien ça ne fonctionne pas. En fait, il n’y a pas moyen de récupérer la clé d’un tableau associatif en Bash. C’est pour cela que le code suivant est fait :

# Déclaration d'un tableau pour les clés
declare -a keys
# On récupère toutes les clés des disques
keys=(${!disks[@]})

On va de nouveau créer un tableau indexé $keys , et on va récupérer dans celui-ci toutes les clés de notre tableau $disks .

À partir de ce moment là, on pourra récupérer la clé et la valeur de chaque élément indépendamment :

echo "Ma partition /var se nomme ${keys[2]} et est ${disks[${keys[2]}]}"

echo "Mais je peux utiliser ça aussi : ${disks['sda7']}"

Et le résultat de ce script :

sda1 --- Mounted on /, 8.1GiB (6.7GiB free)
sda7 --- Mounted on /tmp, 360.0MiB (358.0MiB free)
sda5 --- Mounted on /var, 2.7GiB (1.7GiB free)
sda8 --- Mounted on /home, 131.3GiB (130.8GiB free)
Ma partition /var se nomme sda5 et est Mounted on /var, 2.7GiB (1.7GiB free)
Mais je peux utiliser ça aussi : Mounted on /tmp, 360.0MiB (358.0MiB free)

On dit merci qui ?

En bonus

Comme je suis un mec bien, je vais vous donner une autre petite astuce sur les tableaux : Il est possible de connaître le nombre d’éléments que possède un tableau :

echo "Le tableau possède ${#disks[@]} éléments"
# Le tableau possède 4 éléments

Enjoy !