Comment utiliser eval dans les scripts Linux Bash

Publié: 2022-08-22
Ordinateur portable Linux affichant une invite bash
fatmawati achmad zaenuri/Shutterstock.com

De toutes les commandes Bash, le pauvre vieil eval a probablement la pire réputation. Justifié, ou juste mauvaise presse ? Nous discutons de l'utilisation et des dangers de cette commande Linux la moins appréciée.

Nous devons parler d'eval

Utilisé avec insouciance, eval peut entraîner un comportement imprévisible et même des insécurités du système. D'après les sons, nous ne devrions probablement pas l'utiliser, n'est-ce pas ? Eh bien pas tout à fait.

Vous pourriez dire quelque chose de similaire à propos des automobiles. Entre de mauvaises mains, ils sont une arme mortelle. Les gens les utilisent dans des raids de bélier et comme véhicules de fuite. Devrions-nous tous arrêter d'utiliser des voitures ? Non bien sûr que non. Mais ils doivent être utilisés correctement et par des personnes qui savent les conduire.

Comment travailler avec des variables dans Bash
CONNEXION Comment travailler avec des variables dans Bash

L'adjectif usuel appliqué à eval est « mal ». Mais tout dépend de la façon dont il est utilisé. La commande eval rassemble les valeurs d'une ou plusieurs variables. Il crée une chaîne de commande. Il exécute ensuite cette commande. Cela le rend utile lorsque vous devez faire face à des situations où le contenu d'une commande est dérivé dynamiquement lors de l'exécution de votre script.

Des problèmes surviennent lorsqu'un script est écrit pour utiliser eval sur une chaîne qui a été reçue de quelque part en dehors du script. Il peut être saisi par un utilisateur, envoyé via une API, étiqueté sur une requête HTTPS ou n'importe où ailleurs en dehors du script.

Si la chaîne sur laquelle eval va travailler n'a pas été dérivée localement et par programmation, il existe un risque que la chaîne contienne des instructions malveillantes intégrées ou d'autres entrées mal formées. Évidemment, vous ne voulez pas eval exécute des commandes malveillantes. Donc, pour être sûr, n'utilisez pas eval avec des chaînes générées en externe ou une entrée utilisateur.

Premiers pas avec eval

La commande eval est une commande intégrée du shell Bash. Si Bash est présent, eval sera présent.

eval concatène ses paramètres en une seule chaîne. Il utilisera un seul espace pour séparer les éléments concaténés. Il évalue les arguments, puis passe la chaîne entière au shell pour qu'il s'exécute.

Créons une variable appelée wordcount .

 wordcount="wc -w raw-notes.md"

La variable de chaîne contient une commande pour compter les mots dans un fichier appelé "raw-notes.md".

Nous pouvons utiliser eval pour exécuter cette commande en lui transmettant la valeur de la variable.

 eval " $wordcount "

Utiliser eval avec une variable de chaîne pour compter les mots dans un fichier

La commande est exécutée dans le shell courant, pas dans un sous-shell. Nous pouvons facilement le montrer. Nous avons un court fichier texte appelé "variables.txt". Il contient ces deux lignes.

 premier=Comment faire
deuxième=geek

Nous utiliserons cat pour envoyer ces lignes à la fenêtre du terminal. Ensuite, nous utiliserons eval pour évaluer une commande cat afin que les instructions contenues dans le fichier texte soient exécutées. Cela définira les variables pour nous.

 chat variables.txt
eval "$(cat variables.txt)"
echo $premier $deuxième 

Accéder aux variables définies par eval dans le shell actuel

En utilisant echo pour imprimer les valeurs des variables, nous pouvons voir que la commande eval s'exécute dans le shell actuel, pas dans un sous-shell.

Un processus dans un sous-shell ne peut pas modifier l'environnement shell du parent. Étant donné que eval s'exécute dans le shell actuel, les variables définies par eval sont utilisables depuis le shell qui a lancé la commande eval .

Notez que si vous utilisez eval dans un script, le shell qui serait modifié par eval est le sous-shell dans lequel le script s'exécute, et non le shell qui l'a lancé.

CONNEXION: Comment utiliser les commandes Linux cat et tac

Utilisation de variables dans la chaîne de commande

Nous pouvons inclure d'autres variables dans les chaînes de commande. Nous allons définir deux variables pour contenir des entiers.

 nombre1=10 
num2=7

Nous allons créer une variable pour contenir une commande expr qui renverra la somme de deux nombres. Cela signifie que nous devons accéder aux valeurs des deux variables entières dans la commande. Notez les backticks autour de l'instruction expr .

 add="`expr $num1 + $num2`"

Nous allons créer une autre commande pour nous montrer le résultat de l'instruction expr .

 show="écho"

Notez que nous n'avons pas besoin d'inclure d'espace à la fin de la chaîne echo , ni au début de la chaîne expr . eval s'en charge.

Et pour exécuter toute la commande, nous utilisons :

 évaluer $montrer $ajouter 

Utilisation de variables dans la chaîne de commande

Les valeurs des variables à l'intérieur de la chaîne expr sont remplacées dans la chaîne par eval , avant qu'elle ne soit transmise au shell pour être exécutée.

CONNEXION: Comment travailler avec des variables dans Bash

Accéder aux variables à l'intérieur des variables

Vous pouvez affecter une valeur à une variable, puis affecter le nom de cette variable à une autre variable. En utilisant eval , vous pouvez accéder à la valeur contenue dans la première variable, à partir de son nom qui est la valeur stockée dans la deuxième variable. Un exemple vous aidera à démêler cela.

Copiez ce script dans un éditeur et enregistrez-le dans un fichier appelé "assign.sh".

 #!/bin/bash

title="Comment Geek"
page Web=titre
commande="echo"
eval $commande \${$page Web}

Nous devons le rendre exécutable avec la commande chmod .

 chmod +x assign.sh 

Utiliser chmod pour rendre un script exécutable

Vous devrez le faire pour tous les scripts que vous copiez à partir de cet article. Utilisez simplement le nom de script approprié dans chaque cas.

Lorsque nous exécutons notre script, nous voyons le texte de la variable title même si la commande eval utilise la variable webpage .

 ./assign.sh 

Accéder à la valeur d'une variable à partir de son nom stocké dans une autre variable

Le signe dollar échappé « $ » et les accolades « {} » obligent eval à examiner la valeur contenue dans la variable dont le nom est stocké dans la variable de webpage .

Utilisation de variables créées dynamiquement

Nous pouvons utiliser eval pour créer dynamiquement des variables. Ce script s'appelle "loop.sh".

 #!/bin/bash

totale=0
label="Boucle terminée. Total :"

pour n dans {1..10}
fais
  eval x$n=$n
  echo "Boucle" $x$n
  ((total+=$x$n))
Fini

écho $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10

echo $étiquette $total

Il crée une variable appelée total qui contient la somme des valeurs des variables que nous créons. Il crée ensuite une variable de chaîne appelée label . Il s'agit d'une simple chaîne de texte.

Nous allons boucler 10 fois et créer 10 variables appelées x1 jusqu'à x10 . L'instruction eval dans le corps de la boucle fournit le "x" et prend la valeur du compteur de boucle $n pour créer le nom de la variable. En même temps, il met la nouvelle variable à la valeur du compteur de boucle $n .

9 exemples de boucles for dans les scripts Linux Bash
CONNEXION 9 Exemples de boucles for dans les scripts Linux Bash

Il imprime la nouvelle variable dans la fenêtre du terminal, puis incrémente la variable total avec la valeur de la nouvelle variable.

En dehors de la boucle, les 10 nouvelles variables sont imprimées une fois de plus, toutes sur une seule ligne. Notez que nous pouvons également faire référence aux variables par leurs noms réels, sans utiliser de version calculée ou dérivée de leurs noms.

Enfin, nous imprimons la valeur de la variable total .

 ./loop.sh 

Utiliser eval pour créer dynamiquement des variables

CONNEXION: Primer: Bash Loops: for, while et until

Utiliser eval avec des tableaux

Imaginez un scénario dans lequel vous avez un script qui dure longtemps et qui effectue un certain traitement pour vous. Il écrit dans un fichier journal avec un nom créé à partir d'un horodatage. Parfois, il démarre un nouveau fichier journal. Lorsque le script est terminé, s'il n'y a pas eu d'erreurs, il supprime les fichiers journaux qu'il a créés.

Vous ne voulez pas qu'il se contente de rm *.log , vous voulez seulement qu'il supprime les fichiers journaux qu'il a créés. Ce script simule cette fonctionnalité. Il s'agit de "clear-logs.sh".

 #!/bin/bash

déclarer -a fichiers journaux

nombre de fichiers=0 
rm_string="echo"

function créer_fichierjournal() {
  ((++nombre de fichiers))
  nom_fichier=$(date +"%Y-%m-%d_%H-%M-%S").log
  logfiles[$filecount]=$filename
  echo $filecount "Créé" ${logfiles[$filecount]}
}

# corps du script. Certains traitements sont effectués ici qui
# génère périodiquement un fichier journal. On va simuler ça
créer_fichierjournal
dormir 3
créer_fichierjournal
dormir 3
créer_fichierjournal
dormir 3
créer_fichierjournal

# y a-t-il des fichiers à supprimer ?
pour ((file=1; file<=$filecount; file++))
fais
  # supprimer le fichier journal
  eval $rm_string ${fichiers journaux[$fichier]} "supprimé..."
  fichiers journaux[$file]=""
Fini

Le script déclare un tableau appelé logfiles . Cela contiendra les noms des fichiers journaux créés par le script. Il déclare une variable appelée filecount . Cela contiendra le nombre de fichiers journaux qui ont été créés.

Il déclare également une chaîne appelée rm_string . Dans un script réel, cela contiendrait la commande rm , mais nous utilisons echo afin de pouvoir démontrer le principe de manière non destructive.

La fonction create_logfile() est l'endroit où chaque fichier journal est nommé et où il serait ouvert. Nous créons uniquement le nom de fichier et prétendons qu'il a été créé dans le système de fichiers.

La fonction incrémente la variable filecount . Sa valeur initiale est zéro, donc le premier nom de fichier que nous créons est stocké en position un dans le tableau. Ceci est fait exprès, ainsi voir plus tard.

Le nom de fichier est créé à l'aide de la commande date et de l'extension « .log ». Le nom est stocké dans le tableau à la position indiquée par filecount . Le nom est imprimé dans la fenêtre du terminal. Dans un script réel, vous créeriez également le fichier réel.

Comment mettre en pause un script Bash avec la commande Linux Sleep
CONNEXION Comment mettre en pause un script Bash avec la commande Linux Sleep

Le corps du script est simulé à l'aide de la commande sleep . Il crée le premier fichier journal, attend trois secondes, puis en crée un autre. Il crée quatre fichiers journaux, espacés de manière à ce que les horodatages de leurs noms de fichiers soient différents.

Enfin, il y a une boucle qui supprime les fichiers journaux. Le fichier de compteur de boucles est défini sur un. Il compte jusqu'à et y compris la valeur de filecount , qui contient le nombre de fichiers qui ont été créés.

Si filecount est toujours défini sur zéro, car aucun fichier journal n'a été créé, le corps de la boucle ne sera jamais exécuté car un n'est pas inférieur ou égal à zéro. C'est pourquoi la variable filecount a été mise à zéro lorsqu'elle a été déclarée et pourquoi elle a été incrémentée avant la création du premier fichier.

À l'intérieur de la boucle, nous utilisons eval avec notre rm_string non destructif et le nom du fichier qui est récupéré à partir du tableau. Nous définissons ensuite l'élément de tableau sur une chaîne vide.

C'est ce que nous voyons lorsque nous exécutons le script.

 ./clear-logs.sh 

Suppression de fichiers dont les noms sont stockés dans un tableau

Tout n'est pas mauvais

eval tant décrié a définitivement ses utilisations. Comme la plupart des outils, utilisé imprudemment, il est dangereux, et à plus d'un titre.

Si vous vous assurez que les chaînes sur lesquelles il fonctionne sont créées en interne et non capturées par des humains, des API ou des choses comme les requêtes HTTPS, vous éviterez les principaux pièges.

CONNEXION: Comment afficher la date et l'heure dans le terminal Linux (et l'utiliser dans les scripts Bash)