Cum se utilizează eval în scripturile Linux Bash
Publicat: 2022-08-22 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ă.
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 "
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
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
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
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
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
.
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
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.
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
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)