Оновлення: Деякі люди кажуть, що слід, коли-небудь, використовувати eval. Я не погоджуюсь. Я думаю, що ризик виникає, коли можна передати корумпований внесок eval. Однак є багато поширених ситуацій, коли це не є ризиком, і тому варто знати, як використовувати eval у будь-якому випадку. Ця відповідь stackoverflow пояснює ризики eval та альтернативи eval. Зрештою, користувач повинен визначити, чи / коли eval безпечний та ефективний у використанні.
Оператор bash evalдозволяє виконувати рядки коду, обчислені чи придбані, за допомогою вашого bash script.
Мабуть, найпростішим прикладом може бути програма bash, яка відкриває інший bash-скрипт у вигляді текстового файлу, читає кожен рядок тексту та використовує evalдля їх виконання по порядку. Це, по суті, така ж поведінка, як і bash- sourceоператор, для чого можна було б скористатися, якщо тільки не було необхідності здійснити якусь трансформацію (наприклад, фільтрування чи заміну) вмісту імпортованого сценарію.
Мені це рідко потрібно eval, але мені здається корисним читати чи писати змінні, імена яких містилися в рядках, призначених іншим змінним. Наприклад, виконувати дії над наборами змінних, зберігаючи при цьому код слід невеликим і уникаючи надмірності.
evalконцептуально проста. Однак суворий синтаксис мови bash та порядок розбору інтерпретатора bash можуть бути нюансовані та зробити їх evalвиразними і важкими у використанні чи розумінні. Ось основні елементи:
Переданий аргумент eval- це рядковий вираз, який обчислюється під час виконання. evalвиконає остаточний проаналізований результат свого аргументу як фактичний рядок коду у вашому сценарії.
Синтаксис та порядок розбору є суворими. Якщо результат не є виконуваною лінією bash-коду, в межах вашого сценарію програма вийде з ладу на evalоператор, коли він намагається виконати сміття.
Під час тестування ви можете замінити evalоператор echoі подивитися, що відображається. Якщо він є законним кодом у поточному контексті, його запуск evalбуде працювати.
Наступні приклади можуть допомогти з’ясувати, як працює eval ...
Приклад 1:
eval оператор перед "звичайним" кодом - це NOP
$ eval a=b
$ eval echo $a
b
У наведеному вище прикладі перші evalтвердження не мають мети і можуть бути усунені. evalв першому рядку безглуздо, оскільки немає динамічного аспекту коду, тобто він вже розібрався в кінцеві рядки коду bash, таким чином, він був би ідентичним як звичайний вислів коду в скрипті bash. 2-й evalтеж є безглуздим, оскільки, хоча існує крок розбору, перетворюючи $aйого в буквальний еквівалент рядка, немає ніякого опосередкування (наприклад, відсутність посилань через значення рядка фактичного іменника bash або змінної скрипта, що міститься в bash), тому він би поводився однаково як рядок коду без evalпрефікса.
Приклад 2:
Виконуйте призначення var, використовуючи імена var, передані як значення рядка.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Якби ви були echo $key=$val, вихід був би:
mykey=myval
Це , будучи кінцевим результатом розбору рядків, - це те, що буде виконано eval, отже, результат констатації ехо в кінці ...
Приклад 3:
Додавання додаткового непрямого до Прикладу 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Вищенаведене трохи складніше, ніж попередній приклад, більше спираючись на порядок розбору та особливостей bash. evalЛінія буде грубо отримати розібраний внутрішньо в наступному порядку (зверніть увагу на наступні твердження псевдокод, а не справжній код, просто спроба показати , як заяву буде ламаються на кроки всередині , щоб прийти до кінцевого результату) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Якщо передбачуваний порядок розбору не пояснює, що eval робить достатньо, третій приклад може описати синтаксичний розбір більш детально, щоб допомогти з’ясувати, що відбувається.
Приклад 4:
З’ясуйте, чи містять рядки, назви яких містяться у рядках, самі значення рядків.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
У першій ітерації:
varname="myvarname_a"
Bash аналізує аргумент до evalта evalбачить буквально це під час виконання:
eval varval=\$$myvarname_a
Наступний псевдокод намагається проілюструвати, як bash інтерпретує вищевказаний рядок реального коду, щоб дійти до кінцевого значення, виконаного eval. (наступні рядки описові, не точний баш-код):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Після того, як буде зроблено весь синтаксичний аналіз, результат - це те, що виконується, і його ефект очевидний, демонструючи, що немає нічого особливо загадкового в evalсобі, а складність полягає в синтаксичному аналізі його аргументу.
varval="User-provided"
Код, що залишився у наведеному вище прикладі, просто перевіряє, чи є значення, присвоєне $ varval, нульовим, і, якщо так, спонукає користувача ввести значення.
$($n)працює$nв передпласті. Він намагається запустити команду,1якої не існує.