Cum se utilizează eval în scripturile Linux Bash

Publicat: 2022-08-22
Laptop Linux afișează un prompt bash
fatmawati achmad zaenuri/Shutterstock.com

Dintre toate comenzile Bash, bietul eval vechi are probabil cea mai proastă reputație. Presă justificată sau doar proastă? Discutăm despre utilizarea și pericolele acestei comenzi Linux cel mai puțin iubite.

Trebuie să vorbim despre eval

Folosit cu neglijență, eval poate duce la un comportament imprevizibil și chiar la nesiguranțe ale sistemului. Din sunetele lui, probabil că nu ar trebui să-l folosim, nu? Ei bine, nu chiar.

Ați putea spune ceva asemănător despre automobile. În mâini greșite, sunt o armă mortală. Oamenii le folosesc în raid-uri și ca vehicule de evadare. Ar trebui să încetăm cu toții să mai folosim mașinile? Nu, desigur că nu. Dar trebuie să fie folosite în mod corespunzător și de către oameni care știu să le conducă.

Cum să lucrați cu variabile în Bash
LEGATE Cum să lucrați cu variabile în Bash

Adjectivul obișnuit aplicat eval este „rău”. Dar totul se reduce la modul în care este folosit. Comanda eval adună valorile de la una sau mai multe variabile. Acesta creează un șir de comandă. Apoi execută acea comandă. Acest lucru îl face util atunci când trebuie să faceți față situațiilor în care conținutul unei comenzi este derivat dinamic în timpul execuției scriptului.

Problemele apar atunci când un script este scris pentru a utiliza eval pe un șir care a fost primit din afara scriptului. Poate fi introdus de un utilizator, trimis printr-un API, etichetat pe o solicitare HTTPS sau oriunde altundeva extern scriptului.

Dacă șirul la care va funcționa eval nu a fost derivat local și programatic, există riscul ca șirul să conțină instrucțiuni rău intenționate încorporate sau alte intrări prost formate. Evident, nu doriți ca eval să execute comenzi rău intenționate. Deci, pentru a fi în siguranță, nu utilizați eval cu șiruri generate extern sau cu intrarea utilizatorului.

Primii pași cu eval

Comanda eval este o comandă încorporată în shell Bash. Dacă Bash este prezent, eval va fi prezent.

eval își concatenează parametrii într-un singur șir. Va folosi un singur spațiu pentru a separa elementele concatenate. Evaluează argumentele și apoi trece întregul șir către shell pentru a fi executat.

Să creăm o variabilă numită wordcount .

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

Variabila șir conține o comandă pentru a număra cuvintele dintr-un fișier numit „raw-notes.md”.

Putem folosi eval pentru a executa acea comandă trecându-i valoarea variabilei.

 eval " $wordcount "

Utilizarea eval cu o variabilă șir pentru a număra cuvintele dintr-un fișier

Comanda este executată în shell-ul curent, nu într-un subshell. Putem arăta cu ușurință acest lucru. Avem un fișier text scurt numit „variables.txt”. Conține aceste două rânduri.

 first=Cum se face
secunda=Tocilar

Vom folosi cat pentru a trimite aceste linii către fereastra terminalului. Apoi vom folosi eval pentru a evalua o comandă cat , astfel încât instrucțiunile din fișierul text să fie aplicate. Aceasta va stabili variabilele pentru noi.

 variabile cat.txt
eval „$(cat variabiles.txt)”
echo $prima $secunda 

Accesarea variabilelor stabilite de eval în shell-ul curent

Folosind echo pentru a imprima valorile variabilelor, putem vedea că comanda eval rulează în shell-ul curent, nu într-un subshell.

Un proces dintr-un subshell nu poate schimba mediul shell al părintelui. Deoarece eval rulează în shell-ul curent, variabilele stabilite de eval sunt utilizabile din shell-ul care a lansat comanda eval .

Rețineți că, dacă utilizați eval într-un script, shell-ul care ar fi modificat de eval este subshell-ul în care rulează scriptul, nu shell-ul care l-a lansat.

LEGATE: Cum să utilizați comenzile Linux cat și tac

Utilizarea variabilelor în șirul de comandă

Putem include și alte variabile în șirurile de comenzi. Vom seta două variabile pentru a păstra numere întregi.

 num1=10 
num2=7

Vom crea o variabilă care să dețină o comandă expr care va returna suma a două numere. Aceasta înseamnă că trebuie să accesăm valorile celor două variabile întregi din comandă. Observați backtickurile din jurul instrucțiunii expr .

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

Vom crea o altă comandă pentru a ne arăta rezultatul instrucțiunii expr .

 show="echo"

Rețineți că nu trebuie să includem un spațiu la sfârșitul șirului de echo și nici la începutul șirului expr . eval se ocupă de asta.

Și pentru a executa întreaga comandă folosim:

 eval $show $add 

Utilizarea variabilelor în șirul de comandă

Valorile variabilelor din interiorul șirului expr sunt înlocuite în șir prin eval , înainte de a fi transmise shell-ului pentru a fi executate.

LEGATE: Cum să lucrați cu variabile în Bash

Accesarea variabilelor din interiorul variabilelor

Puteți atribui o valoare unei variabile și apoi să atribuiți numele acelei variabile altei variabile. Folosind eval , puteți accesa valoarea deținută în prima variabilă, din numele acesteia care este valoarea stocată în a doua variabilă. Un exemplu te va ajuta să descurci asta.

Copiați acest script într-un editor și salvați-l ca fișier numit „assign.sh”.

 #!/bin/bash

title="Cum să faci un geek"
webpage=title
comanda="echo"
eval $comanda \${$pagina web}

Trebuie să-l facem executabil cu comanda chmod .

 chmod +x assign.sh 

Folosind chmod pentru a face un script executabil

Va trebui să faceți acest lucru pentru toate scripturile pe care le copiați din acest articol. Folosiți doar numele de script corespunzător în fiecare caz.

Când rulăm scriptul, vedem textul din title variabilei, chiar dacă comanda eval folosește webpage variabilă.

 ./assign.sh 

Accesarea valorii unei variabile din numele acesteia stocate într-o altă variabilă

Semnul dolar „ $ ” și acoladele „ {} ” determină eval să se uite la valoarea deținută în interiorul variabilei al cărei nume este stocat în variabila webpage .

Utilizarea variabilelor create dinamic

Putem folosi eval pentru a crea variabile dinamic. Acest script se numește „loop.sh”.

 #!/bin/bash

total=0
label="Buclă finalizată. Total:"

pentru n în {1..10}
do
  eval x$n=$n
  echo "Buclă" $x$n
  ((total+=$x$n))
Terminat

eco $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10

echo $label $total

Se creează o variabilă numită total care conține suma valorilor variabilelor pe care le creăm. Apoi creează o variabilă șir numită label . Acesta este un șir simplu de text.

Vom face bucla de 10 ori și vom crea 10 variabile numite x1 până la x10 . Declarația eval din corpul buclei oferă „x” și ia valoarea contorului buclei $n pentru a crea numele variabilei. În același timp, setează noua variabilă la valoarea contorului buclei $n .

9 Exemple de bucle for în scripturile Linux Bash
LEGATE 9 Exemple de bucle for în scripturile Bash Linux

Tipărește noua variabilă în fereastra terminalului și apoi incrementează variabila total cu valoarea noii variabile.

În afara buclei, cele 10 noi variabile sunt imprimate încă o dată, toate pe o singură linie. Rețineți că ne putem referi la variabile și prin numele lor reale, fără a folosi o versiune calculată sau derivată a numelor lor.

În cele din urmă, imprimăm valoarea variabilei total .

 ./buclă.sh 

Utilizarea eval pentru a crea dinamic variabile

LEGATE: Primer: Bucle Bash: for, while, and until

Folosind eval With Arrays

Imaginați-vă un scenariu în care aveți un script care rulează de lungă durată și efectuează unele procesări pentru dvs. Acesta scrie într-un fișier jurnal cu un nume creat dintr-o ștampilă de timp. Ocazional, va începe un nou fișier jurnal. Când scriptul s-a terminat, dacă nu au existat erori, acesta șterge fișierele jurnal pe care le-a creat.

Nu doriți să rm *.log , ci doar să ștergeți fișierele jurnal pe care le-a creat. Acest script simulează această funcționalitate. Acesta este „clear-logs.sh”.

 #!/bin/bash

declare -a fișiere jurnal

numărul de fișiere=0 
rm_string="echo"

funcția create_logfile() {
  ((++număr de fișiere))
  nume fișier=$(data +"%Y-%m-%d_%H-%M-%S").log
  logfiles[$filecount]=$nume fișier
  echo $filecount „Creat” ${logfiles[$filecount]}
}

# corpul scenariului. Unele procesări se fac aici că
# generează periodic un fișier jurnal. Vom simula asta
create_logfile
somn 3
create_logfile
somn 3
create_logfile
somn 3
create_logfile

# există fișiere de eliminat?
pentru ((fișier=1; fișier<=$număr de fișiere; fișier++))
do
  # eliminați fișierul jurnal
  eval $rm_string ${logfiles[$file]} „șters...”
  fișiere jurnal[$file]=""
Terminat

Scriptul declară o matrice numită logfiles . Aceasta va păstra numele fișierelor jurnal care sunt create de script. Acesta declară o variabilă numită filecount . Acesta va păstra numărul de fișiere jurnal care au fost create.

De asemenea, declară un șir numit rm_string . Într-un script din lumea reală, acesta ar conține comanda rm , dar folosim echo pentru a putea demonstra principiul într-un mod nedistructiv.

Funcția create_logfile() este locul unde este numit fiecare fișier jurnal și unde ar fi deschis. Creăm doar numele fișierului și pretindem că a fost creat în sistemul de fișiere.

Funcția crește variabila număr de filecount . Valoarea sa inițială este zero, așa că primul nume de fișier pe care îl creăm este stocat la poziția unu în matrice. Acest lucru se face intenționat, așa cum vedeți mai târziu.

Numele fișierului este creat folosind comanda date și extensia „.log”. Numele este stocat în matrice la poziția indicată de filecount . Numele este imprimat în fereastra terminalului. Într-un script din lumea reală, ați crea și fișierul real.

Cum să întrerupeți un script Bash cu comanda Linux Sleep
LEGATE Cum să întrerupeți un script Bash cu comanda Linux Sleep

Corpul scriptului este simulat folosind comanda sleep . Acesta creează primul fișier jurnal, așteaptă trei secunde și apoi creează altul. Acesta creează patru fișiere jurnal, distanțate astfel încât marcajele de timp din numele fișierelor lor să fie diferite.

În cele din urmă, există o buclă care șterge fișierele jurnal. Fișierul contor bucle este setat la unu. Acesta numără până la și inclusiv valoarea filecount , care deține numărul de fișiere care au fost create.

Dacă numărul de filecount este încă setat la zero - deoarece nu au fost create fișiere jurnal - corpul buclei nu va fi niciodată executat deoarece unul nu este mai mic sau egal cu zero. De aceea, variabila filecount a fost setată la zero când a fost declarată și de ce a fost incrementată înainte ca primul fișier să fie creat.

În interiorul buclei, folosim eval cu rm_string -ul nostru nedistructiv și numele fișierului care este preluat din matrice. Apoi setăm elementul de matrice la un șir gol.

Aceasta este ceea ce vedem când rulăm scriptul.

 ./clear-logs.sh 

Ștergerea fișierelor ale căror nume sunt stocate într-o matrice

Nu e totul rău

eval mult defăimat își are cu siguranță utilizările sale. La fel ca majoritatea uneltelor, folosite cu imprudență, este periculos și în mai multe moduri.

Dacă vă asigurați că șirurile pe care funcționează sunt create intern și nu sunt capturate de la oameni, API-uri sau lucruri precum solicitările HTTPS, veți evita capcanele majore.

LEGATE: Cum să afișați data și ora în terminalul Linux (și să le utilizați în scripturile Bash)