Comment utiliser eval dans les scripts Linux Bash
Publié: 2022-08-22 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.
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 "
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
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
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
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
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
.
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
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.
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
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)