Jak używać eval w skryptach Linux Bash

Opublikowany: 2022-08-22
Laptop z systemem Linux wyświetlający monit o bash
fatmawati achmad zaenuri/Shutterstock.com

Ze wszystkich poleceń Bash, biedny stary eval ma prawdopodobnie najgorszą reputację. Uzasadnione, czy po prostu zła prasa? Omawiamy użycie i niebezpieczeństwa tego najmniej lubianego polecenia Linuksa.

Musimy porozmawiać o ewaluacji

Używany niedbale, eval może prowadzić do nieprzewidywalnych zachowań, a nawet niepewności systemu. Sądząc po dźwiękach, prawdopodobnie nie powinniśmy go używać, prawda? No nie do końca.

Można powiedzieć coś podobnego o samochodach. W niewłaściwych rękach są śmiercionośną bronią. Ludzie używają ich w najazdach na taran i jako pojazdy do ucieczki. Czy wszyscy powinniśmy przestać używać samochodów? Nie, oczywiście nie. Ale muszą być używane właściwie i przez ludzi, którzy wiedzą, jak je prowadzić.

Jak pracować ze zmiennymi w Bash
POWIĄZANE Jak pracować ze zmiennymi w Bash

Zwykłym przymiotnikiem stosowanym do eval jest „zło”. Ale wszystko sprowadza się do tego, jak jest używany. Polecenie eval zestawia wartości z jednej lub więcej zmiennych. Tworzy ciąg poleceń. Następnie wykonuje to polecenie. Jest to przydatne, gdy musisz poradzić sobie z sytuacjami, w których treść polecenia jest wyprowadzana dynamicznie podczas wykonywania skryptu.

Problemy pojawiają się, gdy skrypt jest pisany z użyciem eval na łańcuchu, który został odebrany spoza skryptu. Może być wpisany przez użytkownika, wysłany przez API, oznaczony w żądaniu HTTPS lub w dowolnym innym miejscu poza skryptem.

Jeśli ciąg, na którym ma działać eval , nie został wyprowadzony lokalnie i programowo, istnieje ryzyko, że ciąg może zawierać osadzone złośliwe instrukcje lub inne źle sformułowane dane wejściowe. Oczywiście nie chcesz, aby eval wykonywał złośliwe polecenia. Aby być bezpiecznym, nie używaj eval z zewnętrznie generowanymi ciągami lub danymi wejściowymi użytkownika.

Pierwsze kroki z oceną

Polecenie eval jest wbudowanym poleceniem powłoki Bash. Jeśli Bash jest obecny, eval będzie obecny.

eval łączy swoje parametry w jeden ciąg. Użyje jednej spacji do oddzielenia połączonych elementów. Ocenia argumenty, a następnie przekazuje cały ciąg do wykonania powłoki.

Stwórzmy zmienną o nazwie wordcount .

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

Zmienna łańcuchowa zawiera polecenie do zliczania słów w pliku o nazwie „raw-notes.md”.

Możemy użyć eval do wykonania tego polecenia, przekazując mu wartość zmiennej.

 eval " $wordcount "

Używanie eval ze zmienną łańcuchową do zliczania słów w pliku

Polecenie jest wykonywane w bieżącej powłoce, a nie w podpowłoce. Możemy to łatwo pokazać. Mamy krótki plik tekstowy o nazwie „variables.txt”. Zawiera te dwie linie.

 pierwszy=Jak to zrobić
drugi=Geek

Użyjemy cat do wysłania tych linii do okna terminala. Następnie użyjemy eval do oceny polecenia cat , aby wykonać instrukcje zawarte w pliku tekstowym. To ustawi dla nas zmienne.

 cat zmienne.txt
eval "$(cat zmienne.txt)"
echo $pierwsza $druga 

Dostęp do zmiennych ustawionych przez eval w bieżącej powłoce

Używając echo do drukowania wartości zmiennych, możemy zobaczyć, że polecenie eval działa w bieżącej powłoce, a nie w podpowłoce.

Proces w podpowłoce nie może zmienić środowiska powłoki rodzica. Ponieważ eval działa w bieżącej powłoce, zmienne ustawione przez eval mogą być używane z powłoki, która uruchomiła polecenie eval .

Zauważ, że jeśli użyjesz eval w skrypcie, powłoka, która zostanie zmieniona przez eval , jest podpowłoką, w której działa skrypt, a nie powłoką, która go uruchomiła.

POWIĄZANE: Jak korzystać z poleceń cat i tac systemu Linux

Używanie zmiennych w ciągu poleceń

W ciągach poleceń możemy uwzględnić inne zmienne. Ustawimy dwie zmienne do przechowywania liczb całkowitych.

 liczba1=10 
liczba2=7

Stworzymy zmienną do przechowywania polecenia expr , które zwróci sumę dwóch liczb. Oznacza to, że musimy uzyskać dostęp do wartości dwóch zmiennych całkowitych w poleceniu. Zwróć uwagę na backticki wokół expr .

 add="`wyrażenie $numer1 + $numer2`"

Stworzymy kolejne polecenie, aby pokazać nam wynik expr .

 pokaż="echo"

Zauważ, że nie musimy umieszczać spacji na końcu ciągu echo ani na początku ciągu expr . eval się tym zajmuje.

A do wykonania całego polecenia używamy:

 eval $pokaż $dodaj 

Używanie zmiennych w ciągu poleceń

Wartości zmiennych wewnątrz łańcucha expr są podstawiane do łańcucha przez eval , zanim zostaną przekazane do powłoki do wykonania.

POWIĄZANE: Jak pracować ze zmiennymi w Bash

Uzyskiwanie dostępu do zmiennych wewnątrz zmiennych

Możesz przypisać wartość do zmiennej, a następnie przypisać nazwę tej zmiennej do innej zmiennej. Używając eval , możesz uzyskać dostęp do wartości przechowywanej w pierwszej zmiennej, począwszy od jej nazwy, która jest wartością przechowywaną w drugiej zmiennej. Przykład pomoże ci to rozwikłać.

Skopiuj ten skrypt do edytora i zapisz go jako plik o nazwie „assign.sh”.

 #!/kosz/bash

title="Jak to zrobić"
strona internetowa=tytuł
polecenie="echo"
eval $polecenie \${$strona internetowa}

Musimy uczynić go wykonywalnym poleceniem chmod .

 chmod +x przypisz.sh 

Używanie chmod do tworzenia skryptu wykonywalnego

Musisz to zrobić dla wszystkich skryptów, które skopiujesz z tego artykułu. Po prostu użyj odpowiedniej nazwy skryptu w każdym przypadku.

Kiedy uruchamiamy nasz skrypt, widzimy tekst z title zmiennej, mimo że polecenie eval używa zmiennej webpage .

 ./przypisz.sh 

Uzyskiwanie dostępu do wartości zmiennej z jej nazwy przechowywanej w innej zmiennej

Uciekły znak dolara „ $ ” i nawiasy klamrowe „ {} ” powodują, że eval sprawdza wartość przechowywaną wewnątrz zmiennej, której nazwa jest przechowywana w zmiennej webpage .

Korzystanie ze zmiennych tworzonych dynamicznie

Możemy użyć eval do dynamicznego tworzenia zmiennych. Ten skrypt nazywa się „loop.sh”.

 #!/kosz/bash

suma=0
label="Zapętlanie zakończone. Razem:"

dla n w {1..10}
robić
  oszacowanie x$n=$n
  echo "Pętla" $x$n
  ((ogółem+=$x$n))
Gotowe

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

echo $etykieta $łącznie

Tworzy zmienną o nazwie total , która przechowuje sumę wartości tworzonych przez nas zmiennych. Następnie tworzy zmienną łańcuchową o nazwie label . To jest prosty ciąg tekstu.

Zapętlimy się 10 razy i utworzymy 10 zmiennych o nazwach od x1 do x10 . Instrukcja eval w treści pętli zawiera „x” i pobiera wartość licznika pętli $n , aby utworzyć nazwę zmiennej. Jednocześnie ustawia nową zmienną na wartość licznika pętli $n .

9 przykładów pętli for w skryptach Bash w Linuksie
POWIĄZANE 9 przykładów pętli for w skryptach Linux Bash

Drukuje nową zmienną w oknie terminala, a następnie zwiększa total zmienną o wartość nowej zmiennej.

Poza pętlą wypisywanych jest jeszcze 10 nowych zmiennych, wszystkie w jednym wierszu. Zauważ, że możemy odwoływać się do zmiennych również ich prawdziwymi nazwami, bez używania obliczonej lub pochodnej wersji ich nazw.

Na koniec wypisujemy wartość zmiennej total .

 ./pętla.sh 

Używanie eval do dynamicznego tworzenia zmiennych

POWIĄZANE: Primer: Bash Loops: na, chwilę i do

Używanie eval z tablicami

Wyobraź sobie scenariusz, w którym masz skrypt, który działa długo i wykonuje dla Ciebie pewne przetwarzanie. Zapisuje do pliku dziennika o nazwie utworzonej na podstawie znacznika czasu. Czasami uruchamia nowy plik dziennika. Po zakończeniu skryptu, jeśli nie wystąpiły żadne błędy, usuwa utworzone pliki dziennika.

Nie chcesz po prostu rm *.log , chcesz tylko usunąć pliki dziennika, które utworzył. Ten skrypt symuluje tę funkcjonalność. To jest „clear-logs.sh”.

 #!/kosz/bash

zadeklaruj -a logfiles

liczba plików=0 
rm_string="echo"

function create_logfile() {
  ((++liczba plików))
  filename=$(data +"%Y-%m-%d_%H-%M-%S").log
  logfiles[$filecount]=$nazwapliku
  echo $filecount "Utworzono" ${logfiles[$filecount]}
}

# treść skryptu. Tutaj odbywa się pewne przetwarzanie, które
# okresowo generuje plik dziennika. Zasymulujemy to
utwórz_logfile
spać 3
utwórz_logfile
spać 3
utwórz_logfile
spać 3
utwórz_logfile

# czy są jakieś pliki do usunięcia?
for ((plik=1; plik<=$liczbaplików; plik++))
robić
  # usuń plik dziennika
  eval $rm_string ${logfiles[$plik]} "usunięty..."
  pliki dziennika[$plik]=""
Gotowe

Skrypt deklaruje tablicę zwaną logfiles . Będzie to zawierało nazwy plików dziennika, które są tworzone przez skrypt. Deklaruje zmienną o nazwie filecount . To będzie przechowywać liczbę plików dziennika, które zostały utworzone.

Deklaruje również ciąg o nazwie rm_string . W prawdziwym skrypcie zawierałoby to polecenie rm , ale używamy echo , aby zademonstrować zasadę w niedestrukcyjny sposób.

Funkcja create_logfile() nazwę każdego pliku dziennika i miejsce, w którym zostanie on otwarty. Tworzymy tylko nazwę pliku i udajemy, że została utworzona w systemie plików.

Funkcja zwiększa zmienną filecount . Jego wartość początkowa to zero, więc pierwsza tworzona przez nas nazwa pliku jest przechowywana na pozycji jeden w tablicy. Odbywa się to celowo, jak zobaczymy później.

Nazwa pliku jest tworzona za pomocą polecenia date i rozszerzenia „.log”. Nazwa jest przechowywana w tablicy w pozycji wskazanej przez filecount . Nazwa jest wypisywana w oknie terminala. W prawdziwym skrypcie utworzyłbyś również rzeczywisty plik.

Jak wstrzymać skrypt Bash za pomocą polecenia Linux Sleep?
POWIĄZANE Jak wstrzymać skrypt Bash za pomocą polecenia Linux Sleep

Ciało skryptu jest symulowane za pomocą polecenia sleep . Tworzy pierwszy plik dziennika, czeka trzy sekundy, a następnie tworzy kolejny. Tworzy cztery pliki dziennika, rozmieszczone w odstępach, aby sygnatury czasowe w ich nazwach plików były różne.

Wreszcie istnieje pętla, która usuwa pliki dziennika. Plik licznika pętli jest ustawiony na jeden. Liczy się do wartości filecount , która przechowuje liczbę utworzonych plików.

Jeśli filecount jest nadal ustawiona na zero — ponieważ nie zostały utworzone żadne pliki dziennika — treść pętli nigdy nie zostanie wykonana, ponieważ jeden jest nie mniejszy lub równy zero. Dlatego zmienna filecount była ustawiona na zero, kiedy została zadeklarowana i dlaczego została zwiększona przed utworzeniem pierwszego pliku.

Wewnątrz pętli używamy eval z naszym nieniszczącym rm_string i nazwą pliku, który jest pobierany z tablicy. Następnie ustawiamy element tablicy na pusty ciąg.

Oto, co widzimy, gdy uruchamiamy skrypt.

 ./clear-logs.sh 

Usuwanie plików, których nazwy są przechowywane w tablicy

Nie wszystko jest złe

Bardzo złośliwa eval zdecydowanie ma swoje zastosowanie. Jak większość narzędzi, używany lekkomyślnie, jest niebezpieczny i to na wiele sposobów.

Jeśli upewnisz się, że ciągi, na których działa, są tworzone wewnętrznie i nie są przechwytywane od ludzi, interfejsów API lub takich rzeczy, jak żądania HTTPS, unikniesz głównych pułapek.

POWIĄZANE: Jak wyświetlić datę i godzinę w terminalu Linux (i używać jej w skryptach Bash)