Hoe eval te gebruiken in Linux Bash-scripts

Hoe eval te gebruiken in Linux Bash-scripts
fatmawati achmad zaenuri/Shutterstock.com

Van alle Bash-commando’s, arme ouwe eval heeft waarschijnlijk de slechtste reputatie. Gerechtvaardigd, of gewoon slechte pers? We bespreken het gebruik en de gevaren van deze minst geliefde Linux-commando’s.

We moeten praten over eval

onzorgvuldig gebruikt, eval kan leiden tot onvoorspelbaar gedrag en zelfs systeemonzekerheden. Zoals het klinkt, zouden we het waarschijnlijk niet moeten gebruiken, toch? Nou niet helemaal.

Je zou iets soortgelijks kunnen zeggen over auto’s. In de verkeerde handen zijn ze een dodelijk wapen. Mensen gebruiken ze bij ramkraken en als vluchtvoertuigen. Moeten we allemaal stoppen met het gebruik van auto’s? Nee natuurlijk niet. Maar ze moeten op de juiste manier worden gebruikt en door mensen die weten hoe ze ermee moeten rijden.

Werken met variabelen in Bash

VERWANTWerken met variabelen in Bash

Het gebruikelijke adjectief toegepast op eval is “kwaad”. Maar het komt allemaal neer op hoe het wordt gebruikt. De eval commando verzamelt de waarden van een of meer variabelen. Het maakt een opdrachtreeks aan. Vervolgens voert het dat commando uit. Dit maakt het handig wanneer u situaties moet aanpakken waarin de inhoud van een opdracht dynamisch wordt afgeleid tijdens de uitvoering van uw script.

Er ontstaan ​​problemen wanneer een script wordt geschreven om te gebruiken eval op een string die ergens vandaan is ontvangen buiten het script. Het kan worden ingetypt door een gebruiker, verzonden via een API, getagd op een HTTPS-verzoek of ergens anders buiten het script.

Als de string dat eval gaat werken niet lokaal en programmatisch is afgeleid, bestaat het risico dat de tekenreeks ingesloten kwaadaardige instructies of andere slecht gevormde invoer bevat. Natuurlijk wil je niet eval om kwaadaardige opdrachten uit te voeren. Dus voor de zekerheid niet gebruiken eval met extern gegenereerde strings of gebruikersinvoer.

Eerste stappen met eval

De eval commando is een ingebouwd Bash-shellcommando. Als Bash aanwezig is, eval zal present zijn.

eval voegt zijn parameters samen tot een enkele string. Het zal een enkele spatie gebruiken om aaneengeschakelde elementen te scheiden. Het evalueert de argumenten en geeft vervolgens de hele string door aan de shell om uit te voeren.

Laten we een variabele maken met de naam wordcount.

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

De stringvariabele bevat een opdracht om de woorden te tellen in een bestand met de naam “raw-notes.md”.

We kunnen gebruiken eval om dat commando uit te voeren door het door te geven aan de waarde van de variabele.

eval "$wordcount"

Eval gebruiken met een stringvariabele om de woorden in een bestand te tellen

De opdracht wordt uitgevoerd in de huidige shell, niet in een subshell. We kunnen dit eenvoudig laten zien. We hebben een kort tekstbestand met de naam ‘variables.txt’. Het bevat deze twee regels.

first=How-To
second=Geek

We gebruiken cat om deze regels naar het terminalvenster te sturen. Dan gebruiken we eval evalueren cat commando zodat de instructies in het tekstbestand worden uitgevoerd. Dit zal de variabelen voor ons instellen.

cat variables.txt
eval "$(cat variables.txt)"
echo $first $second

Toegang tot variabelen ingesteld door eval in de huidige shell

Door het gebruiken van echo om de waarden van de variabelen af ​​te drukken, kunnen we zien dat eval opdracht wordt uitgevoerd in de huidige shell, niet in een subshell.

Een proces in een subshell kan de shell-omgeving van de parent niet veranderen. Omdat eval in de huidige shell wordt uitgevoerd, zijn de variabelen ingesteld door eval zijn bruikbaar vanaf de shell die de . lanceerde eval opdracht.

Houd er rekening mee dat als u eval in een script, de shell die zou worden gewijzigd door eval is de subshell waarin het script wordt uitgevoerd, niet de shell waarmee het is gestart.

Variabelen gebruiken in de opdrachtreeks

We kunnen andere variabelen opnemen in de opdrachtreeksen. We stellen twee variabelen in om gehele getallen te bevatten.

num1=10 
num2=7

We zullen een variabele maken om een ​​vast te houden expr commando dat de som van twee getallen teruggeeft. Dit betekent dat we toegang moeten hebben tot de waarden van de twee integer-variabelen in de opdracht. Let op de backticks rond de expr uitspraak.

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

We zullen nog een commando maken om ons het resultaat van de . te laten zien expr uitspraak.

show="echo"

Merk op dat we geen spatie aan het einde van de hoeven toe te voegen echo string, noch aan het begin van de expr snaar. eval zorgt daarvoor.

En om het hele commando uit te voeren gebruiken we:

eval $show $add

Variabelen gebruiken in de opdrachtreeks

De variabele waarden binnen de expr string worden in de string vervangen door eval voordat het wordt doorgegeven aan de shell die moet worden uitgevoerd.

Toegang tot variabelen binnen variabelen

U kunt een waarde aan een variabele toewijzen en vervolgens de naam van die variabele naar een andere variabele. Gebruik makend van evalhebt u toegang tot de waarde gehouden in de eerste variabele, van zijn naam die de . is waarde opgeslagen in de tweede variabele. Een voorbeeld zal je helpen dat te ontwarren.

Kopieer dit script naar een editor en sla het op als een bestand met de naam ‘assign.sh’.

#!/bin/bash

title="How-To Geek"
webpage=title
command="echo"
eval $command ${$webpage}

We moeten het uitvoerbaar maken met de chmod opdracht.

chmod +x assign.sh

chmod gebruiken om een ​​script uitvoerbaar te maken

U moet dit doen voor alle scripts die u uit dit artikel kopieert. Gebruik in elk geval gewoon de juiste scriptnaam.

Wanneer we ons script uitvoeren, zien we de tekst van de variabele title ook al is de eval commando gebruikt de variabele webpage.

./assign.sh

Toegang krijgen tot de waarde van een variabele via de naam die is opgeslagen in een andere variabele

Het ontsnapte dollarteken “$” en de beugels “{}” laat eval kijken naar de waarde in de variabele waarvan de naam is opgeslagen in de webpage variabel.

Dynamisch gecreëerde variabelen gebruiken

We kunnen gebruiken eval om dynamisch variabelen te creëren. Dit script wordt “loop.sh” genoemd.

#!/bin/bash

total=0
label="Looping complete. Total:"

for n in {1..10}
do
  eval x$n=$n
  echo "Loop" $x$n
  ((total+=$x$n))
done

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

echo $label $total

Het creëert een variabele genaamd total die de som bevat van de waarden van de variabelen die we maken. Het creëert dan een stringvariabele genaamd label. Dit is een eenvoudige reeks tekst.

We gaan 10 keer herhalen en 10 variabelen maken met de naam x1 tot x10. De eval statement in de hoofdtekst van de lus levert de “x” op en neemt de waarde van de lusteller aan $n om de variabelenaam te maken. Tegelijkertijd stelt het de nieuwe variabele in op de waarde van de lusteller $n.

9 voorbeelden van for-loops in Linux Bash-scripts

VERWANT9 voorbeelden van for-loops in Linux Bash-scripts

Het drukt de nieuwe variabele af naar het terminalvenster en verhoogt vervolgens de total variabele met de waarde van de nieuwe variabele.

Buiten de lus worden de 10 nieuwe variabelen nog een keer afgedrukt, allemaal op één regel. Merk op dat we ook naar de variabelen kunnen verwijzen met hun echte naam, zonder een berekende of afgeleide versie van hun naam te gebruiken.

Ten slotte printen we de waarde van de total variabel.

./loop.sh

Eval gebruiken om dynamisch variabelen te maken

Eval gebruiken met arrays

Stel je een scenario voor waarin je een script hebt dat lang loopt en enige verwerking voor je uitvoert. Het schrijft naar een logbestand met een naam die is gemaakt op basis van een tijdstempel. Af en toe zal het een nieuw logbestand starten. Wanneer het script klaar is en er geen fouten zijn opgetreden, verwijdert het de logbestanden die het heeft gemaakt.

Je wilt het niet zomaar rm *.log, je wilt alleen dat het de logbestanden verwijdert die het heeft gemaakt. Dit script simuleert die functionaliteit. Dit is “clear-logs.sh.”

#!/bin/bash

declare -a logfiles

filecount=0 
rm_string="echo"

function create_logfile() {
  ((++filecount))
  filename=$(date +"%Y-%m-%d_%H-%M-%S").log
  logfiles[$filecount]=$filename
  echo $filecount "Created" ${logfiles[$filecount]}
}

# body of the script. Some processing is done here that
# periodically generates a log file. We'll simulate that
create_logfile
sleep 3
create_logfile
sleep 3
create_logfile
sleep 3
create_logfile

# are there any files to remove?
for ((file=1; file<=$filecount; file++))
do
  # remove the logfile
  eval $rm_string ${logfiles[$file]} "deleted..."
  logfiles[$file]=""
done

Het script declareert een array genaamd logfiles . Dit bevat de namen van de logbestanden die door het script zijn gemaakt. Het declareert een variabele genaamd filecount . Dit bevat het aantal logbestanden dat is gemaakt.

Het declareert ook een string genaamd rm_string. In een real-world script zou dit de bevatten rm commando, maar we gebruiken echo zodat we het principe op een niet-destructieve manier kunnen demonstreren.

De functie create_logfile() is waar elk logbestand wordt genoemd en waar het zou worden geopend. We creëren alleen de bestandsnaamen doen alsof het is gemaakt in het bestandssysteem.

De functie verhoogt de filecount variabel. De beginwaarde is nul, dus de eerste bestandsnaam die we maken, wordt opgeslagen op positie één in de array. Dit is met opzet gedaan, zie ook verderop.

De bestandsnaam wordt aangemaakt met de date commando en de extensie “.log”. De naam wordt opgeslagen in de array op de positie aangegeven door filecount. De naam wordt afgedrukt naar het terminalvenster. In een real-world script zou je ook het eigenlijke bestand maken.

Een bash-script pauzeren met het Linux-slaapcommando

VERWANTEen bash-script pauzeren met het Linux-slaapcommando

De hoofdtekst van het script wordt gesimuleerd met behulp van de sleep opdracht. Het maakt het eerste logbestand aan, wacht drie seconden en maakt dan een ander logbestand aan. Het creëert vier logbestanden, zo verdeeld dat de tijdstempels in hun bestandsnamen verschillend zijn.

Ten slotte is er een lus die de logbestanden verwijdert. Het lustellerbestand is ingesteld op één. Het telt tot en met de waarde van filecountdie het aantal bestanden bevat dat is gemaakt.

Als filecount is nog steeds op nul gezet – omdat er geen logbestanden zijn gemaakt – zal de loop-body nooit worden uitgevoerd omdat één niet kleiner is dan of gelijk is aan nul. Dat is waarom de filecount variabele was op nul gezet toen deze werd gedeclareerd en waarom deze werd verhoogd voordat het eerste bestand is gemaakt.

Binnen de lus gebruiken we eval met onze niet-destructieve rm_string en de naam van het bestand dat uit de array wordt opgehaald. Vervolgens stellen we het array-element in op een lege string.

Dit is wat we zien als we het script uitvoeren.

./clear-logs.sh

Bestanden verwijderen waarvan de namen in een array zijn opgeslagen

Het is niet allemaal slecht

Veel verguisd eval heeft zeker zijn nut. Zoals de meeste gereedschappen, is het roekeloos gebruikt gevaarlijk, en in meer dan één opzicht.

Als je ervoor zorgt dat de strings waarop het werkt intern worden gemaakt en niet worden vastgelegd door mensen, API’s of dingen zoals HTTPS-verzoeken, vermijd je de grote valkuilen.

Nieuwste artikelen

Gerelateerde artikelen