Les programmeurs utilisent régulièrement bash pour résoudre de nombreuses tâches liées au développement de logiciels. Dans le même temps, les tableaux bash sont souvent considérés comme l'une des fonctionnalités les plus incompréhensibles de ce shell (probablement, les tableaux sont en deuxième position derrière les expressions régulières à cet égard). L'auteur du matériel, dont nous publions la traduction aujourd'hui, invite tout le monde dans le monde merveilleux des tableaux bash qui, si vous vous habituez à leur syntaxe inhabituelle, peut apporter de nombreux avantages.
Le vrai défi que les tableaux bash sont utiles
Écrire sur bash est controversé. Le fait est que les articles sur bash se transforment souvent en guides d'utilisation qui sont consacrés à des histoires sur les fonctionnalités syntaxiques des commandes en question. Cet article est écrit différemment, nous espérons que vous ne le trouverez pas dans le prochain "manuel d'utilisation".
Compte tenu de ce qui précède, imaginez un scénario réel pour l'utilisation de tableaux dans bash. Supposons que vous soyez confronté à la tâche d'évaluer et d'optimiser un utilitaire à partir d'un nouvel ensemble interne d'outils utilisés dans votre entreprise. Dans la première étape de cette étude, vous devez le tester avec différents ensembles de paramètres. Le test vise à étudier le comportement d'un nouvel ensemble d'outils lorsqu'ils utilisent un nombre différent de threads. Pour simplifier la présentation, nous supposons que la «boîte à outils» est une «boîte noire» compilée à partir du code C ++. Lors de son utilisation, le seul paramètre que nous pouvons influencer est le nombre de threads réservés au traitement des données. L'appel du système sous enquête à partir de la ligne de commande ressemble à ceci:
./pipeline
Les bases
Tout d'abord, nous déclarons un tableau contenant les valeurs du paramètre
--threads
avec lequel nous voulons tester le système. Ce tableau ressemble à ceci:
allThreads=(1 2 4 8 16 32 64 128)
Dans cet exemple, tous les éléments sont des nombres, mais en fait, dans les tableaux bash, vous pouvez stocker à la fois des nombres et des chaînes. Par exemple, la déclaration d'un tel tableau est tout à fait acceptable:
myArray=(1 2 "three" 4 "five")
Comme pour les autres variables bash, assurez-vous qu'il n'y a pas d'espace autour du signe
=
. Sinon, bash considérera le nom de la variable comme le nom du programme dont il a besoin pour exécuter, et
=
son premier argument!
Maintenant que nous avons initialisé le tableau, extrayons-en quelques éléments. Ici, vous pouvez remarquer, par exemple, que la
echo $allThreads
ne sortira que le premier élément du tableau.
Afin de comprendre les raisons de ce comportement, s'écartons un peu des tableaux et rappelons comment travailler avec des variables dans bash. Prenons l'exemple suivant:
type="article" echo "Found 42 $type"
Supposons que vous ayez une variable
$type
qui contient une chaîne qui représente un nom. Après ce mot, ajoutez la lettre
s
. Cependant, vous ne pouvez pas simplement ajouter cette lettre à la fin du nom de la variable, car cela transformera la commande pour accéder à la variable en
$types
, c'est-à-dire que nous travaillerons avec une variable complètement différente. Dans cette situation, vous pouvez utiliser une construction comme
echo "Found 42 "$type"s"
. Mais il est préférable de résoudre ce problème en utilisant des accolades:
echo "Found 42 ${type}s"
, ce qui nous permet de dire à bash où le nom de variable commence et se termine (fait intéressant, la même syntaxe est utilisée dans JavaScript ES6 pour incorporer des variables dans les expressions dans les
chaînes de modèle ).
Revenons maintenant aux tableaux. Il s'avère que bien que les accolades ne soient généralement pas nécessaires lorsque vous travaillez avec des variables, elles sont nécessaires pour travailler avec des tableaux. Ils vous permettent de définir des index pour accéder aux éléments du tableau. Par exemple, une commande de la forme
echo ${allThreads[1]}
affichera le deuxième élément du tableau. Si vous oubliez les accolades dans la construction ci-dessus, bash percevra
[1]
comme une chaîne et traitera ce qui se passe en conséquence.
Comme vous pouvez le voir, les tableaux en bash ont une syntaxe étrange, mais en eux, au moins, la numérotation des éléments commence à partir de zéro. Cela les rend similaires aux tableaux de nombreux autres langages de programmation.
Façons d'accéder aux éléments du tableau
Dans l'exemple ci-dessus, nous avons utilisé des indices entiers dans des tableaux qui sont spécifiés explicitement. Considérez maintenant deux autres façons de travailler avec des tableaux.
La première méthode est applicable si nous avons besoin de l'élément
$i
ème du tableau, où
$i
est une variable contenant l'indice de l'élément de tableau souhaité. Vous pouvez extraire cet élément du tableau à l'aide d'une construction de la forme
echo ${allThreads[$i]}
.
La deuxième méthode vous permet d'afficher tous les éléments du tableau. Il consiste à remplacer l'index numérique par le symbole
@
(il peut être interprété comme une commande pointant sur tous les éléments du tableau). Cela ressemble à ceci:
echo ${allThreads[@]}
.
Itération sur les éléments du tableau en boucles
Les principes ci-dessus de travailler avec des éléments de tableau nous seront utiles pour résoudre le problème d'énumération des éléments de tableau. Dans notre cas, cela signifie lancer la commande de
pipeline
étude avec chacune des valeurs, qui symbolise le nombre de threads et est stockée dans un tableau. Cela ressemble à ceci:
for t in ${allThreads[@]}; do ./pipeline --threads $t done
Énumération des indices de tableau dans les boucles
Considérons maintenant une approche légèrement différente du tri des tableaux. Au lieu d'itérer sur les éléments, nous pouvons itérer sur les index du tableau:
for i in ${!allThreads[@]}; do ./pipeline --threads ${allThreads[$i]} done
Analysons ce qui se passe ici. Comme nous l'avons déjà vu, une construction de la forme
${allThreads[@]}
représente tous les éléments du tableau. Lorsque nous ajoutons un point d'exclamation ici, nous transformons cette construction en
${!allThreads[@]}
, ce qui conduit au fait qu'elle renvoie les index du tableau (de 0 à 7 dans notre cas).
En d'autres termes, la boucle
for
tous les index du tableau représentés comme la variable
$i
, et dans le corps de la boucle, les éléments du tableau, qui servent de valeurs au paramètre
--thread
, sont
--thread
à l'aide de la construction
${allThreads[$i]}
.
La lecture de ce code est plus difficile que celle de l'exemple précédent. Par conséquent, la question se pose de savoir à quoi servent toutes ces difficultés. Et nous en avons besoin car dans certaines situations, lors du traitement de tableaux en boucles, vous devez connaître à la fois les indices et les valeurs des éléments. Par exemple, si vous devez ignorer le premier élément d'un tableau, l'itération sur les indices nous évitera, par exemple, la nécessité de créer une variable supplémentaire et de l'incrémenter en boucle pour travailler avec les éléments du tableau.
Remplissage des matrices
Jusqu'à présent, nous avons exploré le système en appelant la commande
pipeline
et en lui passant chacune des valeurs du paramètre
--threads
nous intéressent. Supposons maintenant que cette commande donne la durée d'un certain processus en secondes. Nous aimerions intercepter les données qui lui sont retournées à chaque itération et les enregistrer dans un autre tableau. Cela nous donnera l'occasion de travailler avec les données stockées une fois tous les tests terminés.
Constructions de syntaxe utiles
Avant de parler de la façon d'ajouter des données aux tableaux, regardons quelques constructions de syntaxe utiles. Pour commencer, nous avons besoin d'un mécanisme pour obtenir la sortie de données par les commandes bash. Afin de capturer la sortie d'une commande, vous devez utiliser la construction suivante:
output=$( ./my_script.sh )
Après avoir exécuté cette commande, ce que
myscript.sh
$output
sera stocké dans la variable
$output
.
La deuxième construction, qui sera très utile très prochainement, nous permet d'attacher de nouvelles données aux tableaux. Cela ressemble à ceci:
myArray+=( "newElement1" "newElement2" )
Résolution de problèmes
Maintenant, si nous rassemblons tout ce que nous venons d'apprendre, nous pouvons créer un script pour tester le système qui exécute une commande avec chacune des valeurs de paramètre du tableau et stocke dans l'autre tableau ce que cette commande affiche.
allThreads=(1 2 4 8 16 32 64 128) allRuntimes=() for t in ${allThreads[@]}; do runtime=$(./pipeline --threads $t) allRuntimes+=( $runtime ) done
Et ensuite?
Nous venons d'examiner comment utiliser les tableaux bash pour parcourir les paramètres utilisés lors du démarrage d'un programme et pour enregistrer les données que ce programme renvoie. Cependant, les options d'utilisation des tableaux ne sont pas limitées à ce scénario. Voici quelques autres exemples.
Alertes de problème
Dans ce scénario, nous allons examiner une application qui se décompose en modules. Chacun de ces modules possède son propre fichier journal. Nous pouvons écrire un script de tâche
cron
qui, si des problèmes sont détectés dans le fichier journal correspondant, notifiera par email la personne responsable de chacun des modules:
# - logPaths=("api.log" "auth.log" "jenkins.log" "data.log") logEmails=("jay@email" "emma@email" "jon@email" "sophia@email") # for i in ${!logPaths[@]}; do log=${logPaths[$i]} stakeholder=${logEmails[$i]} numErrors=$( tail -n 100 "$log" | grep "ERROR" | wc -l ) # 5 if [[ "$numErrors" -gt 5 ]]; then emailRecipient="$stakeholder" emailSubject="WARNING: ${log} showing unusual levels of errors" emailBody="${numErrors} errors found in log ${log}" echo "$emailBody" | mailx -s "$emailSubject" "$emailRecipient" fi done
Demandes d'API
Supposons que vous souhaitiez collecter des informations sur les commentaires des utilisateurs sur vos publications sur Medium. Comme nous n'avons pas d'accès direct à la base de données de ce site, nous ne discuterons pas des requêtes SQL. Cependant, vous pouvez utiliser différentes API pour accéder à ce type de données.
Afin d'éviter de longues conversations sur l'authentification et les jetons, nous utiliserons, en tant que point de terminaison, le service de test d'API public
JSONPlaceholder . Après avoir reçu une publication du service et extrait les données de son code aux adresses e-mail des commentateurs, nous pouvons mettre ces données dans un tableau:
endpoint="https://jsonplaceholder.typicode.com/comments" allEmails=() # 10 for postId in {1..10}; do # API response=$(curl "${endpoint}?postId=${postId}") # jq JSON allEmails+=( $( jq '.[].email' <<< "$response" ) ) done
Veuillez noter que l'outil
jq est utilisé ici, ce qui permet d'analyser JSON sur la ligne de commande. Nous n'entrerons pas dans les détails de l'utilisation de jq si vous êtes intéressé par cet outil - consultez la documentation correspondante.
Bash ou Python?
Tableaux - une fonctionnalité utile et elle est disponible non seulement dans bash. Celui qui écrit des scripts pour la ligne de commande peut avoir une question logique sur les situations dans lesquelles il vaut la peine d'utiliser bash et dans quel cas, par exemple, Python.
À mon avis, la réponse à cette question réside dans combien le programmeur dépend d'une technologie particulière. Disons que si le problème peut être résolu directement sur la ligne de commande, rien n'empêche l'utilisation de bash. Cependant, dans le cas où, par exemple, le script qui vous intéresse fait partie d'un projet écrit en Python, vous pouvez bien utiliser Python.
Par exemple, pour résoudre le problème considéré ici, vous pouvez utiliser un script écrit en Python, cependant, cela reviendra à écrire des wrappers pour Python pour bash:
import subprocess all_threads = [1, 2, 4, 8, 16, 32, 64, 128] all_runtimes = [] # for t in all_threads: cmd = './pipeline --threads {}'.format(t) # subprocess , p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) output = p.communicate()[0] all_runtimes.append(output)
Peut-être que la solution à ce problème avec bash, sans impliquer d'autres technologies, est plus courte et plus compréhensible, et ici vous pouvez complètement vous passer de Python.
Résumé
Dans ce document, nous avons analysé de nombreux modèles utilisés pour travailler avec des tableaux. Voici un tableau où vous trouverez ce que nous avons examiné et quelque chose de nouveau.
Construction de syntaxe | La description |
arr=() | Créer un tableau vide |
arr=(1 2 3) | Initialisation du tableau |
${arr[2]} | Obtention du troisième élément d'un tableau |
${arr[@]} | Obtention de tous les éléments du tableau |
${!arr[@]} | Obtention d'indices de tableau |
${#arr[@]} | Calcul de la taille du tableau |
arr[0]=3 | Écraser le premier élément d'un tableau |
arr+=(4) | Rejoindre un tableau de valeurs |
str=$(ls) | Enregistrement de la sortie de la ls sous forme de chaîne |
arr=( $(ls) ) | Enregistrement de la sortie de la ls tant que tableau de noms de fichiers |
${arr[@]:s:n} | Obtention d'éléments de tableau d'un élément avec index s à un élément avec index s+(n-1)
|
À première vue, les tableaux bash peuvent sembler plutôt étranges, mais les possibilités qu'ils offrent en valent la peine pour faire face à ces bizarreries. Nous pensons qu'ayant maîtrisé les tableaux bash, vous les utiliserez assez souvent. Il est facile d'imaginer d'innombrables scénarios dans lesquels ces tableaux peuvent être utiles.
Chers lecteurs! Si vous avez des exemples intéressants d'utilisation de tableaux dans des scripts bash, partagez-les.
